Skip to main content

Compute per-point linearity/planarity/scattering features for LAS/LAZ point clouds with LAS ExtraBytes export and RGB visualization outputs.

Project description

ptcfextract

ptcfextract computes per-point local geometry features from point clouds stored in LAS/LAZ, useful for feature extraction research pipelines:

  • Linearity
  • Planarity
  • Scattering

It can be used as:

  • a library (feature extraction only, integrate with your own pipeline)
  • a ready-to-run pipeline (downsample/outlier removal + visualization + two outputs)
  • a CLI tool (ptcfextract)

Features (What it does)

Core feature extraction

For each point, neighbors within a radius are used to compute a covariance matrix and eigenvalues. The outputs are:

  • linearity = (e2 - e1) / e2
  • planarity = (e1 - e0) / e2
  • scattering = e0 / e2

Robust handling:

  • If a point has too few neighbors → outputs are NaN
  • If covariance is degenerate or division by zero would occur → outputs are NaN
  • Small negative eigenvalues due to numeric noise are clipped to 0

Outputs (only two)

If your output base is out.laz, the tool writes:

Output 1: RGB overwritten with L/P/S

out_rgbLPS_overwriteRGB.laz

  • RGB is overwritten with normalized [L,P,S]
    • R = Linearity
    • G = Planarity
    • B = Scattering
  • All other original point dimensions are preserved

This is for instant visualization in CloudCompare / other viewers.

Output 2: Original RGB kept + scalar fields added

out_addLPS_extraFields_keepOriginalRGB.laz

  • Original RGB remains unchanged
  • Adds three ExtraBytes scalar fields (float32):
    • linearity
    • planarity
    • scattering
  • All other original point dimensions are preserved

This is for analysis workflows where you want to keep original appearance.

Note: If the input has no RGB fields and you request Output 1, ptcfextract upgrades point format to write RGB and prints a warning (some vendor-specific extra dims may not be preserved).


Installation

Standard install

pip install ptcfextract

Recommended for LAZ (compressed) support

pip install "ptcfextract[laz]"

CLI Usage

Basic (defaults)

ptcfextract -i input.laz -o out.laz

Help

ptcfextract --help

Choose neighborhood settings

ptcfextract -i input.laz -o out.laz --radius 0.5 --min-neighbors 15

Enable downsampling (faster for huge clouds)

ptcfextract -i input.laz -o out.laz --downsample --voxel 0.1

Outlier removal options

No outlier removal

ptcfextract -i input.laz -o out.laz --outlier none

Statistical Outlier Removal (SOR)

ptcfextract -i input.laz -o out.laz --outlier sor --sor-nn 20 --sor-std 2.0

Radius Outlier Removal (ROR)

ptcfextract -i input.laz -o out.laz --outlier ror --ror-np 6 --ror-r 0.5

Visualization options

Disable visualization (headless / faster batch runs)

ptcfextract -i input.laz -o out.laz --no-viz

Visualize a single feature

ptcfextract -i input.laz -o out.laz --viz linearity
ptcfextract -i input.laz -o out.laz --viz planarity
ptcfextract -i input.laz -o out.laz --viz scattering

Visualize RGB=[L,P,S]

ptcfextract -i input.laz -o out.laz --viz rgb

Export control (Only two supported outputs)

Disable Output 1 (overwrite RGB with LPS)

ptcfextract -i input.laz -o out.laz --no-overwrite-rgb

Disable Output 2 (add L/P/S scalar fields, keep original RGB)

ptcfextract -i input.laz -o out.laz --no-add-dims

Python Usage (Library)

1) Feature extraction only (Integrate into your own pipeline)

Use this if you want your own:

  • downsampling strategy (e.g., random, uniform, octree)
  • visualization (CloudCompare, PyVista, Open3D custom)
  • ML pipeline / feature engineering
import laspy
import numpy as np
from ptcfextract import compute_lps_features

las = laspy.read("input.laz")
xyz = np.vstack([las.x, las.y, las.z]).T

features = compute_lps_features(xyz, radius=0.5, min_neighbors=10)

linearity = features[:, 0]
planarity = features[:, 1]
scattering = features[:, 2]

2) Use the helper voxel downsampler + feature extraction

import laspy
import numpy as np
from ptcfextract import voxel_downsample_indices, compute_lps_features

las = laspy.read("input.laz")
xyz = np.vstack([las.x, las.y, las.z]).T

idx = voxel_downsample_indices(xyz, voxel_size=0.1)
xyz_ds = xyz[idx]

features_ds = compute_lps_features(xyz_ds, radius=0.5, min_neighbors=10)

3) Apply optional outlier removal indices (but keep full control)

import laspy
import numpy as np
from ptcfextract import outlier_removal_indices, compute_lps_features

las = laspy.read("input.laz")
xyz = np.vstack([las.x, las.y, las.z]).T

inliers = outlier_removal_indices(
    xyz,
    mode="sor",
    sor_nb_neighbors=20,
    sor_std_ratio=2.0,
    ror_nb_points=6,
    ror_radius=0.5,
)

xyz_in = xyz[inliers]
features_in = compute_lps_features(xyz_in, radius=0.5, min_neighbors=10)

4) Convert features to RGB=[L,P,S] (for your own visualization)

from ptcfextract import features_to_rgb

rgb01 = features_to_rgb(features_in)   # Nx3 float in [0,1]

5) Convert a single feature to a colormap RGB (for your own visualization)

from ptcfextract import scalar_to_colormap_rgb

rgb_L = scalar_to_colormap_rgb(features_in[:, 0])  # linearity colormap
rgb_P = scalar_to_colormap_rgb(features_in[:, 1])  # planarity colormap
rgb_S = scalar_to_colormap_rgb(features_in[:, 2])  # scattering colormap

6) Run the full pipeline from Python (downsample→outliers→viz→export)

from ptcfextract import process_pointcloud

process_pointcloud(
    input_path="input.laz",
    output_base="out.laz",
    radius=0.5,
    min_neighbors=10,
    use_downsample=True,
    voxel_size=0.1,
    outlier_mode="sor",
    sor_nb_neighbors=20,
    sor_std_ratio=2.0,
    visualize=True,
    visualize_mode="rgb",
    export_rgb_lps_overwrite=True,
    export_add_dims_keep_original_rgb=True,
)

7) Export features to LAS manually (advanced control)

If you computed features yourself and want to export using the same preservation logic:

import laspy
import numpy as np
from ptcfextract import write_las_preserve_all, compute_lps_features, features_to_rgb

base = laspy.read("input.laz")
xyz = np.vstack([base.x, base.y, base.z]).T
features = compute_lps_features(xyz, radius=0.5, min_neighbors=10)
rgb01 = features_to_rgb(features)

orig_idx = np.arange(xyz.shape[0], dtype=np.int64)

# Output 1: overwrite RGB with LPS
write_las_preserve_all(
    out_path="out_rgbLPS_overwriteRGB.laz",
    base_las=base,
    kept_orig_indices=orig_idx,
    rgb01=rgb01,
    add_lps_dims=False,
)

# Output 2: keep original RGB, add extra dims
write_las_preserve_all(
    out_path="out_addLPS_extraFields_keepOriginalRGB.laz",
    base_las=base,
    kept_orig_indices=orig_idx,
    rgb01=None,
    add_lps_dims=True,
    lps_features=features,
)

Notes & Troubleshooting

Units matter (meters vs feet)

  • radius, voxel, and ror-r are in the same units as the LAS coordinates.
  • If your cloud is in feet, typical values often need to be larger than meter-based defaults.

Many NaNs in features

You’ll see a NaN count printed (pipeline mode). If it’s high:

  • Increase --radius
  • Decrease --min-neighbors
  • Downsample less aggressively (smaller --voxel)
  • If the cloud is sparse, a small radius may not find enough neighbors

Performance tips for huge point clouds

  • Enable downsampling (--downsample --voxel 0.1 or larger)
  • Disable visualization (--no-viz)
  • Consider outlier removal only after downsampling

Input has no RGB fields

  • Output 2 still works (extra dims added, original dims preserved).

  • Output 1 requires RGB, so ptcfextract upgrades point format to write RGB and prints a warning.

    • This can drop some unusual vendor-specific dimensions depending on the LAS point format and library limitations.

Viewing in CloudCompare

  • Output 1: visualize using RGB.

  • Output 2: visualize using scalar fields:

    • linearity, planarity, scattering
    • In CloudCompare: open the scalar field dropdown to choose which to view.

Citation

If you use ptcfextract in academic work, cite the Zenodo DOI associated with the GitHub release.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ptcfextract-0.1.0.tar.gz (13.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ptcfextract-0.1.0-py3-none-any.whl (12.2 kB view details)

Uploaded Python 3

File details

Details for the file ptcfextract-0.1.0.tar.gz.

File metadata

  • Download URL: ptcfextract-0.1.0.tar.gz
  • Upload date:
  • Size: 13.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.13

File hashes

Hashes for ptcfextract-0.1.0.tar.gz
Algorithm Hash digest
SHA256 7d31a305e437f04155186f26ed82dec8d3df86a2e480fd32814340bd1c3de3c3
MD5 24d8b65de9276f25a0758930ee70f426
BLAKE2b-256 d8f8d2d8b96d6bbbd5cb3be4f2ff02a7b878eaa948c74ee73ab0d8961d8a78a1

See more details on using hashes here.

File details

Details for the file ptcfextract-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: ptcfextract-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 12.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.13

File hashes

Hashes for ptcfextract-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4073cbe77ce0c73bccbd27e315ed383775cf5de9ba2e5cb84c7b7e45f09e87fe
MD5 65b0d2696c2d5bfdf15243d17a17d8e7
BLAKE2b-256 90e40c136a8fc2f3ead4727fadb4b3fb1fa67b847dfc8e4e09bd7223ea15b24a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page