The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

PiC: Pointcloud Interactive Computation for Forest Structure Analysis

Version License: GPL v3 R

Overview

PiC (Pointcloud Interactive Computation) is an R package for the processing, segmentation, and analysis of terrestrial and mobile laser scanning (TLS and MLS) forest point-cloud data. It provides fast voxel-based processing, classification of the point cloud into forest floor, understory, wood and canopy components, and algorithms for single-tree analysis and stand-level structural characterization.

The methods are designed to handle large and dense point clouds efficiently, supporting applications in forest structure assessment, connectivity analysis, and fire-risk evaluation. In particular, Forest_seg() was built for very large datasets: it can process clouds of up to ~600 million points (over 10 GB) on a workstation such as a MacBook Pro M3 with 36 GB of RAM, in reasonable times — a throughput of roughly one million points per second. Input data are provided as .xyz, .txt, .las, or .laz point-cloud files.

Key Features

Installation

From CRAN

install.packages("PiC")

Development version

# install.packages("devtools")
devtools::install_github("rupppy/PiC")

System requirements

Optional dependency. Reading and writing .las/.laz files relies on the lidR package, listed under Suggests. If lidR is not installed, PiC still runs on .xyz/.txt data and the LAS/LAZ routines stop with an informative message. Install it with install.packages("lidR").

Quick Start

Forest segmentation

library(PiC)

# Load a point cloud (x, y, z)
data <- data.table::fread("forest_plot.xyz", header = FALSE)
colnames(data)[1:3] <- c("x", "y", "z")

# Full segmentation + tree/plot metrics
results <- Forest_seg(
  a             = data,
  filename      = "plot_001",
  dimVox        = 2,        # voxel size (cm) for LOR wood segmentation
  eps           = 2,        # DBSCAN epsilon (voxel units)
  mpts          = 9,        # DBSCAN minimum points
  calculate_cbh = TRUE,     # estimate crown base height (GAB)
  output_format = "las",    # single classified LAS (default) or "xyz"
  output_path   = "./results"
)

results$tree_metrics      # per-tree table
results$plot_report_file  # path to the plot-level report
results$las_file          # path to the classified LAS file

Single-tree analysis

tree_results <- SegOne(
  a                 = data,
  filename          = "MyTree",
  dimVox            = 2,
  eps               = 1,
  mpts              = 4,
  calculate_metrics = TRUE,
  output_path       = "./output"
)

print(tree_results$metrics)

Metrics from an already-classified cloud

# Recompute tree- and plot-level metrics from a classified LAS
# (classes: ground = 2, understory = 3, wood = 4, crown = 5)
m <- metrics_from_las(
  las_file    = "plot_001_classified.las",
  output_path = "./output"
)

Point-cloud diagnostics (pre-processing)

# Assess density and structure before segmentation
diag <- pic_analyze_cloud(a = data, output_path = "./output")

Interactive Shiny application

run_PiC()

Main Functions

Function Purpose
Forest_seg() End-to-end plot segmentation and tree/plot metrics (8-stage pipeline).
SegOne() Single-tree wood–leaf segmentation and metrics; writes a classified LAS.
metrics_from_las() Recomputes tree/plot metrics from an already-classified LAS, without re-segmenting.
pic_analyze_cloud() Pre-processing diagnostics (point/voxel density, structure), with optional PDF report.
Voxels() Standalone voxelization of a point cloud.
Floseg() Forest-floor (ground) segmentation.
run_PiC() Launches the interactive Shiny interface.

Processing Pipeline (Forest_seg, 8 stages)

  1. Load & shift — input is coerced to an x, y, z table; a global coordinate shift is applied for numerical precision (reversed for the outputs).
  2. Forest floor extraction — a coarse/fine ground model (DTM) separates the forest floor from low vegetation and above-ground biomass (AGB).
  3. LOR (Ligneous Object Recognition) — woody points (trunks and branches) are identified.
  4. Tree detection & metrics — tree positions and heights are derived from the woody clusters.
  5. DBH — diameter at breast height fitted at multiple heights.
  6. DAV (Directional Anisotropy of Vegetation) — foliage is split into crown and understory.
  7. GAB (Geometrical Aggregation of Biomass) — crown base height (CBH) is estimated.
  8. Output — classified point cloud, reports, and parameters log are written.

Point classification scheme

Each point is assigned an integer class, consistent with the codes stored in the classified LAS file:

Code Component
2 Forest floor (ground / vegetal soil)
3 Understory (low vegetation and shrubs below the crown)
4 Wood (trunks and branches)
5 Crown / foliage
6 Non-valid trees (clusters that failed DBH validation)
7 Noise (isolated or non-classifiable points)

Algorithm Details

LOR — Ligneous Object Recognition

AGB points are voxelized (dimVox, th) and grouped with density-based clustering (DBSCAN, parameters eps/mpts). Clusters are retained as woody structures when they exceed a minimum size (N), satisfy a linearity/quality filter (w_linear), and reach a minimum trunk length (h_trunk). A dedicated rescue pass (h_rescue_minh_rescue_max) recovers woody points in the height band where trunks and low branches are most often missed.

DAV — Directional Anisotropy of Vegetation

The remaining foliage is voxelized at canopy_vox_dim and filtered by a minimum density (canopy_min_density). Voxels are clustered (dav_eps/dav_minPts) and split into crown and understory using an adaptive height threshold derived from the median tree height (dav_height_factor, dav_median_height_factor, capped by dav_understory_max_start). Low vegetation from the forest-floor stage is assigned directly to the understory.

GAB — Geometrical Aggregation of Biomass (CBH)

When calculate_cbh = TRUE, crown and wood voxels are merged for spatial continuity and projected onto a hexagonal tessellation (cbh_hex_side). Each foliage cell is assigned to the nearest tree by Voronoi partitioning on voxel coordinates, and a breadth-first connectivity trace (trunk → branches → foliage) identifies the lowest continuously foliated branch, whose height is reported as the crown base height. The step runs in parallel across the available cores. A minimum foliage extent (cbh_min_branch_length) guards against spurious detections.

DBH calculation (multi-height, dual method)

At each priority height (1.3 m, with fallbacks at 1.8 m and 2.3 m) both Pratt and Landau circle-fitting methods are computed; when both are valid, the fit with the lower RMSE is kept. This handles occluded trunks, irregular sections and scan artefacts. A fit is accepted only within the radius range (dbh_min_radiusdbh_max_radius, i.e. ≈ 5–100 cm diameter) and below the maximum RMSE (dbh_max_rmse, default 5 cm). Trees failing all heights are flagged as non-valid (class 6).

Parameters

Parameters are grouped by the forest component they affect. Defaults are those of Forest_seg() 3.3.1.

1. Input / Output

2. Forest floor (DTM-based extraction)

3. Wood detection (LOR)

4. DBH (multi-height diameter)

5. Crown–understory separation (DAV)

6. Crown base height (GAB / CBH)

Output Files

Classified point cloud

Reports (when generate_reports = TRUE)

*_tree_report.csv — one row per tree:

Column Units Description
Tree_n Tree identifier
cls Internal cluster id
X, Y, Z m Position (original coordinates)
Height m Total height above the DTM
DBH (cm) cm Diameter at the selected height
RMSE (cm) cm Circle-fit quality
valid_tree TRUE/FALSE (DBH validation)
CBH m Crown base height (when computed)

*_plot_report.csv — stand-level summary in long format (metric; value), including: area_of_interest_m2, coverage_area_m2, coverage_percentage, crown_volume_m3, understory_volume_m3, min/max/mean/median/sd_height_m, mean/median_dbh_cm, mean/median_cbh_m, tree_count, valid_tree_count, trees_per_hectare, basal_area_m2_ha.

plot_summary <- data.table::fread(results$plot_report_file, sep = ";")
plot_summary[metric == "basal_area_m2_ha", value]

Other outputs

Usage Examples

Example 1 — Standard forest inventory

library(PiC)

data <- data.table::fread("mature_stand.xyz")
colnames(data)[1:3] <- c("x", "y", "z")

results <- Forest_seg(
  a                 = data,
  filename          = "inventory_2025",
  output_path       = "./results",
  integer_precision = "mm",
  dimVox            = 2, th = 2, eps = 2, mpts = 9,
  calculate_cbh     = TRUE,
  output_format     = "las"
)

trees        <- results$tree_metrics
plot_summary <- data.table::fread(results$plot_report_file, sep = ";")

cat(sprintf("Trees: %d\n", nrow(trees)))
cat(sprintf("Mean DBH: %.1f cm\n", mean(trees[valid_tree == TRUE, `DBH (cm)`], na.rm = TRUE)))
cat(sprintf("Basal area: %.2f m2/ha\n", plot_summary[metric == "basal_area_m2_ha", value]))

Example 2 — Fire-risk assessment (ladder fuels)

results <- Forest_seg(
  a                     = "aleppo_pine.xyz",
  filename              = "fire_risk",
  calculate_cbh         = TRUE,    # critical for ladder-fuel detection
  cbh_min_branch_length = 1.2,     # conservative foliage extent
  cbh_save_points       = TRUE,
  output_format         = "las"
)

trees     <- results$tree_metrics
valid_cbh <- trees[CBH > 0 & CBH < 900, CBH]

cat(sprintf("Mean CBH: %.2f m\n", mean(valid_cbh)))
cat(sprintf("Trees with CBH < 2 m: %d  [ladder-fuel risk]\n", sum(valid_cbh < 2)))

Example 3 — Large dataset / speed

results <- Forest_seg(
  a                 = "large_plot.xyz",
  filename          = "optimized",
  integer_precision = "cm",   # coarser coordinate rounding
  dimVox            = 3,       # larger wood voxels
  canopy_vox_dim    = 0.20,    # coarser DAV resolution
  calculate_cbh     = FALSE    # skip the most intensive stage
)

Parameter Tuning Guidelines

By scanner type

Scanner integer_precision dimVox dav_eps
High-res TLS mm 2 2
Standard TLS mm 2–3 2
Mobile LS (MLS) cm 3–4 3
UAV-LiDAR cm 4–5 3–4

By forest type

Type N (voxels) h_trunk (m) w_linear
Young regeneration 2000 1.5 0.4
Mature conifers 3000–5000 3.0 0.5
Broadleaf 3000 2.5 0.45
Sparse / degraded 2000 2.0 0.4

By analysis goal

Fast inventory (DBH + height only):

calculate_cbh = FALSE
generate_reports = TRUE

Fire-risk assessment:

calculate_cbh   = TRUE       # ladder-fuel detection
cbh_save_points = TRUE       # export crown/understory layers
output_format   = "las"

Research (full detail):

calculate_cbh    = TRUE
cbh_save_points  = TRUE
generate_reports = TRUE
Vox_print        = TRUE      # diagnostic voxel grid

Troubleshooting

No trees detected (tree_count = 0)

# Relax wood segmentation
dimVox   = 2     # finer voxels
eps      = 3     # larger clustering radius
N        = 2000  # lower cluster-size threshold
w_linear = 0.4   # accept less linear clusters

Many invalid DBH (valid_tree = FALSE)

# Inspect the fit-quality distribution
hist(results$tree_metrics[["RMSE (cm)"]])

# If systematically high, relax (cautiously):
dbh_max_rmse = 7
# If clusters are noisy, tighten the wood filters (N, h_trunk, w_linear).

Many CBH failures (CBH = -999)

cbh_min_branch_length = 1.0   # accept shorter foliated branches
canopy_min_density    = 300   # lower density requirement
# Verify the DAV crown is well populated (output_format = "xyz" exports it).

Memory errors

integer_precision = "cm"      # coarser coordinate rounding
dimVox            = 3
canopy_vox_dim    = 0.20
# Or subsample very large inputs:
data_sub <- data[sample(.N, 50e6)]

Coordinate Precision & Performance

PiC processes the cloud on native float coordinates and applies a single global shift for numerical stability. The integer_precision argument controls the rounding granularity used throughout the pipeline: "mm" keeps 3 decimals (recommended for high-resolution TLS), while "cm" keeps 2 decimals, which lightens memory use and voxel bookkeeping on large or coarser MLS/UAV clouds. The CBH stage (GAB) runs in parallel across the available cores.

What’s New in 3.3.1

See NEWS.md for the full history, including the 3.3 features (metrics_from_las(), pic_analyze_cloud(), crown/understory volumes, the revised GAB/CBH connectivity and the LOR rescue pass).

Citation

Package

Ferrara, R., & Arrizza, S. (2025). PiC: Pointcloud Interactive Computation
for Forest Structure Analysis. R package version 3.3.1.
https://hdl.handle.net/20.500.14243/533471

Methodological reference

Ferrara, R., Virdis, S.G.P., Ventura, A., Ghisu, T., Duce, P., & Pellizzaro, G. (2018).
An automated approach for wood-leaf separation from terrestrial LIDAR point clouds
using the density based clustering algorithm DBSCAN.
Agricultural and Forest Meteorology, 262, 434-444.
https://doi.org/10.1016/j.agrformet.2018.04.008

Circle-fitting algorithm

Pratt, V. (1987). Direct least-squares fitting of algebraic surfaces.
ACM SIGGRAPH Computer Graphics, 21(4), 145-152.

Authors

Roberto Ferrara (Maintainer) — CNR-IBE (National Research Council of Italy – Institute for BioEconomy) Email: roberto.ferrara@cnr.it ORCID: 0009-0000-3627-6867

Stefano Arrizza (Contributor) — CNR-IBE Email: stefano.arrizza@cnr.it ORCID: 0009-0009-2290-3650

License

Licensed under the GNU General Public License v3.0 or later. See the GPL-3 license for details.

Acknowledgments

Development supported by CNR-IBE research on Mediterranean forest structure and fire-risk assessment using terrestrial and mobile laser scanning.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.