Type: Package
Title: Comparative Shortest Path Forest Stand Segmentation from LiDAR Data
Version: 0.1.2
Description: Functionality for segmenting individual trees from a forest stand scanned with a close-range (e.g., terrestrial or mobile) laser scanner. The complete workflow from a raw point cloud to a complete tabular forest inventory is provided. The package contains several algorithms for detecting tree bases and a graph-based algorithm to attach all remaining points to these tree bases. It builds heavily on the 'lidR' package. A description of the segmentation algorithm can be found in Larysch et al. (2025) <doi:10.1007/s10342-025-01796-z>.
URL: https://github.com/JulFrey/CspStandSegmentation
BugReports: https://github.com/JulFrey/CspStandSegmentation/issues
License: GPL-3
Encoding: UTF-8
Imports: Rcpp (≥ 1.0.5), lidR, dbscan, igraph, foreach, doParallel, magrittr, data.table, sf, terra, RCSF, conicfit, rgl
LinkingTo: Rcpp, RcppArmadillo, lidR, BH
RoxygenNote: 7.3.3
Depends: R (≥ 4.1.0)
Suggests: testthat (≥ 3.0.0)
Config/testthat/edition: 3
NeedsCompilation: yes
Packaged: 2025-10-23 15:37:21 UTC; Frey
Author: Julian Frey ORCID iD [aut, cre], Zoe Schindler ORCID iD [ctb]
Maintainer: Julian Frey <julian.frey@wwd.uni-freiburg.de>
Repository: CRAN
Date/Publication: 2025-10-28 12:40:08 UTC

Add geometric features to a LAS object

Description

The function calls a fast cpp multi-core function to calculate eigenvalues for the points in a point cloud based on the k nearest neighbors. Afterwards it adds geometric features like Curvature, Linearity, Planarity, Sphericity, Anisotrophy and Verticlity to the points itself.

Usage

add_geometry(las, k = 10L, n_cores = 1)

Arguments

las

A LAS object (see lidR::LAS)

k

the k nearest neighbors to use for the eigenvalue calculation

n_cores

The number of CPU cores to use

Details

Details of the metrics can be found in: Hackel, T., Wegner, J.D. & Schindler, K. (2016) Contour Detection in Unstructured 3D Point Clouds. In 2016 IEEE Conference on Computer Vision and Pattern Recognition (CVPR). Presented at the 2016 IEEE Conference on Computer Vision and Pattern Recognition (CVPR), IEEE, Las Vegas, NV, USA, pp. 1610-1618.

Value

The function returns a single LAS object with the geometric features attached to it in the LAS@data section.

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

Examples


LASfile <- system.file("extdata", "beech.las", package="CspStandSegmentation")
las <- lidR::readLAS(LASfile, select = "xyz")

las <- add_geometry(las, k = 5, n_cores = 2)
summary(las@data)



Add all las_attributes from las@data to the header of a las element

Description

The helper function adds all headings from las@data which are not part of lidR:::LASATTRIBUTES to the las header using lidR::add_lasattribute. Only attributes that are included in the header got saved when using lidR::writeLAS, this is a convenient way to add them.

Usage

add_las_attributes(las)

Arguments

las

an element of lidR::LAS class

Value

the las file with updated header

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

Examples


file <- system.file("extdata", "beech.las", package="CspStandSegmentation")
las <- lidR::readTLSLAS(file)

las@data$noise <- runif(nrow(las@data))
las@data$noiseZ <- las@data$var1 * las@data$Z

las <- add_las_attributes(las)


Add voxel coordinates to a las file

Description

Adds the collums x_vox, y_vox and z_vox in the given ressolution to the las element. This is convenient if information has been derived in voxel space and these should be attached to the original points.

Usage

add_voxel_coordinates(las, res)

Arguments

las

an element of lidR::LAS class

res

voxel ressolution in [m]

Details

Voxel coordinates derived with this function are identical to those derived by lidR::voxelize.

Value

las file with additional voxel coordinates

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

Examples


file = system.file("extdata", "beech.las", package="CspStandSegmentation")
las = lidR::readTLSLAS(file)

las <- add_voxel_coordinates(las,res = 1)



helper function for csp_cost_segemntation

Description

The function performs a Dijkstra algorithm on a 3D voxel file to assign every voxel to the closest seed point using the igraph package.

Usage

comparative_shortest_path(
  vox = vox,
  adjacency_df = adjacency_df,
  seeds,
  v_w = 0,
  l_w = 0,
  s_w = 0,
  N_cores = parallel::detectCores() - 1,
  Voxel_size
)

Arguments

vox

a LAS S4 element with XYZ voxel coordinates in the @data slot.

adjacency_df

a data.frame with voxel ids (row numbers) in the first column and a neighboring voxel ID in the second column and the weight (distance) in the third column. Might be generated using the dbscan::frNN function (which requires reshaping the data).

seeds

seed points for tree positions.

v_w, l_w, s_w

weights for verticality, linearity spericity see csp_cost_segmentation

N_cores

Number of CPU cores for multi-threading

Voxel_size

Edge length used to create the voxels. This is only important to gain comparable distance weights on different voxel sizes. Should be greater than 0.

Value

voxels with the TreeID in the data slot

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

See Also

csp_cost_segmentation


Comparative Shortest Path with cost weighting tree segmentation

Description

Segments single trees from forest point clouds based on tree positions (xyz-coordinates) provided in the map argument.

Usage

csp_cost_segmentation(
  las,
  map,
  Voxel_size = 0.3,
  V_w = 0,
  L_w = 0,
  S_w = 0,
  N_cores = 1
)

Arguments

las

A lidR LAS S4 object.

map

A data.frame, including the columns X, Y, Z, TreeID, with X and Y depicting the location of the trees. Can be generated using CspStandSegmentation::find_base_coordinates_raster

Voxel_size

The voxel size (3D resolution) for the routing graph to determine the nearest map location for every point in the point cloud.

V_w

verticality weight. Since trunks are vertical structures, routing through voxels with high verticality can be rated 'cheaper.' should be a number between 0 and 1 with 0 meaning no benefit for more vertical structures.

L_w

Linearity weight. Similar to V_w but for linearity, higher values indicate a malus for linear shapes (usually branches).

S_w

Spericity weight. Similar to V_w but for sphericity, higher values indicate a malus for spherical shapes (usually small branches and leaves).

N_cores

number of CPU cores used for parallel routing using the foreach package.

Details

The whole point cloud is voxelized in the given resolution and the center of gravity for the points inside is calculated as voxel coordinate. A graph is build, which connects the voxel coordinates based on a db-scan algorithm. The distances between the voxel coordinates is weighted based on geometric features computed for the points in the voxels. Distances along planar and/or vertical faces like stems are weighted shorter than distances through voxels with high sphericity like leaves and clusters of twigs. This avoids small individuals spreading into the upper canopy. For every voxel center, the weighted distance in the network is calculated to all tree locations from the map argument. The TreeID of the map argument with the shortest distance is assigned to the voxel. All points in the point cloud receive the TreeID from their parent voxel.

Value

Returns a copy of the las point cloud with an additional field for the TreeID.

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

See Also

comparative_shortest_path

Examples


# read example data

file = system.file("extdata", "beech.las", package="CspStandSegmentation")
las = lidR::readTLSLAS(file)

# Find tree positions as starting points for segmentation
map <- CspStandSegmentation::find_base_coordinates_raster(las)

# segment trees
segmented <- las |>
CspStandSegmentation::add_geometry() |>
CspStandSegmentation::csp_cost_segmentation(map, 1, S_w = 0.5)





Fast Eigenvalues decomposition for k nearest neighbors using a C++ function

Description

C++ helper function to compute eigenvalues for geometric feature calculation.

Usage

eigen_decomposition(las, k, ncpu = 1L)

Arguments

las

LAS element

k

k nearest neighbors

ncpu

number of cpu cores to use

Value

The function returns for every point the 3 eigenvalues and the third element of the third eigenvector. These values are needed to compute planarity, linerity, verticality etc. in the add_geometry function

Author(s)

Julian Frey <julian.frey@iww.uni-freiburg.de>

See Also

add_geometry


helper function to unlist IDs generated by dbscan::frNN

Description

creates a vector of indices from a nested list created by dbscan::frNN

Usage

fast_unlist(list, l)

Arguments

list

a list element created by dbscan::frNN

l

the expected length of the result

Value

Returns a vector with the values in the list.

Author(s)

Julian Frey <julian.frey@iww.uni-freiburg.de>


helper function to unlist distances computed by dbscan::frNN

Description

extracts the distances from a nested list created by dbscan::frNN

Usage

fast_unlist_dist(list, l)

Arguments

list

a list element created by dbscan::frNN

l

the expected length of the result

Value

Returns a vector with the values in the list.

Author(s)

Julian Frey <julian.frey@iww.uni-freiburg.de>


Farthest Distance Sampling (Farthest Point Sampling)

Description

This function selects n points from a matrix of points such that the minimum distance between any two points is maximized. This version is memory efficient and can handle large matrices.

Usage

fds(mat, n, ret = "idx", scale = FALSE)

Arguments

mat

a matrix of points with one row for each point and one column for each dimension, can also be a las object then only XYZ will be used

n

the number of points to select, or if <1 the proportion of points to select

ret

the type of output to return. Options are "idx" (default) to return the indices of the selected points, "mat" to return the selected points.

scale

logical. If TRUE, the dimensions are scaled to have a mean of 0 and a standard deviation of 1 before calculating distances.

Value

a vector of indices or a matrix of points

Examples

mat <- matrix(rnorm(1000), ncol = 10)
sample <- fds(mat, 50, ret = "mat")
str(sample)



Find stem base position using a geometric feature filtering and clustering approach

Description

Find stem base position using a geometric feature filtering and clustering approach

Usage

find_base_coordinates_geom(
  las,
  zmin = 0.5,
  zmax = 2,
  res = 0.5,
  min_verticality = 0.9,
  min_planarity = 0.5,
  min_cluster_size = NULL
)

Arguments

las

an element of lidR::LAS class

zmin

lower search boundary

zmax

upper search boundary

res

cluster search radius

min_verticality

minimum verticality >0 & <1 for a point to be considered a stem point

min_planarity

minimum planarity >0 & <1 for a point to be considered a stem point

min_cluster_size

minimum number of points in the cluster to be considered a tree, if NULL median cluster size is chosen

Value

data.frame with X, Y, Z and TreeID for stem base positions

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

Examples

# read example data
file = system.file("extdata", "beech.las", package="CspStandSegmentation")
tls = lidR::readTLSLAS(file)

# Find tree positions
map <- CspStandSegmentation::find_base_coordinates_geom(tls)

Find stem base position using a density raster approach

Description

Find stem base position using a density raster approach

Usage

find_base_coordinates_raster(
  las,
  res = 0.1,
  zmin = 0.5,
  zmax = 2,
  q = 0.975,
  eps = 0.2
)

Arguments

las

an element of lidR::LAS class

res

raster resolution

zmin

lower search boundary

zmax

upper search boundary

q

quantile of raster density to assign a tree region

eps

search radius to merge base points

Value

data.frame with X, Y, Z and TreeID for stem base positions

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

Examples

# read example data
file = system.file("extdata", "beech.las", package="CspStandSegmentation")
tls = lidR::readTLSLAS(file)

# Find tree positions
map <- CspStandSegmentation::find_base_coordinates_raster(tls)

Function to perform a forest inventory based on a segmented las object (needs to contain TreeID)

Description

This function estimates a taper curve for evry tree and returns the DBH at 1.3m, its position in XY coordinates, the tree height and the trees 2D projection area.

Usage

forest_inventory(
  las,
  slice_min = 0.3,
  slice_max = 4,
  increment = 0.2,
  width = 0.1,
  max_dbh = 1,
  n_cores = max(c(1, parallel::detectCores()/2 - 1))
)

Arguments

las

lidR las object with the segmented trees

slice_min

the minimum height of a slice for stems to estimate the taper curve

slice_max

the maximum height of a slice for stems to estimate the taper curve

increment

the increment of the slices

width

the width of the slices

max_dbh

the maximum DBH allowed

n_cores

number of cores to use

Value

a data.frame with the TreeID, X, Y, DBH, quality_flag, Height and ConvexHullArea

Examples


# read example data
file = system.file("extdata", "beech.las", package="CspStandSegmentation")
las = lidR::readTLSLAS(file)

# find tree positions as starting points for segmentation
map <- CspStandSegmentation::find_base_coordinates_raster(las)

# segment trees
segmented <- las |>
  CspStandSegmentation::add_geometry(n_cores = 2) |>
  CspStandSegmentation::csp_cost_segmentation(map, 1, N_cores = 2)

# perform inventory
inventory <- CspStandSegmentation::forest_inventory(segmented, n_cores = 2)


Function to perform a forest inventory based on a segmented las object (needs to contain TreeID) This version is a faster but more simplistic approach than forest_inventory() for the DBH estimates

Description

Function to perform a forest inventory based on a segmented las object (needs to contain TreeID) This version is a faster but more simplistic approach than forest_inventory() for the DBH estimates

Usage

forest_inventory_simple(
  las,
  slice_min = 1.2,
  slice_max = 1.4,
  max_dbh = 1,
  n_cores = max(c(1, parallel::detectCores()/2 - 1))
)

Arguments

las

lidR las object with the segmented trees

slice_min

the minimum height of a DBH slice

slice_max

the maximum height of a DBH slice

max_dbh

the maximum DBH allowed

n_cores

number of cores to use

Value

a data.frame with the TreeID, X, Y, DBH, quality_flag, Height and ConvexHullArea


Makes one las-object from multiple las-objects

Description

This function merges multiple las objects into one las object. The function checks if all inputs are las-objects and if they have the same CRS. The function will also add a column oci with the original cloud index to each las-object. The function will then rbind all data by the minimum set of columns. If the fill argument is set to False, columns which do not exist in all las objects will be removed. If the fill argument is set to True, missing columns will be filled with NA.

Usage

las_merge(..., oci = TRUE, fill = FALSE)

Arguments

...

any number of las objects

oci

add a column with the original cloud index

fill

fill missing columns with NA if it is set to False columns which do not exist in all las-objects will be removed

Value

A single las-object with only the overlapping column name

Examples

las1 <- lidR::LAS(data.frame(X = runif(100), Y = runif(100), Z = runif(100)))
las2 <- lidR::LAS(data.frame(X = runif(100) + 2, Y = runif(100), Z = runif(100)))
las3 <- lidR::LAS(data.frame(X = runif(100) + 4, Y = runif(100), Z = runif(100)))
merged <- las_merge(las1, las2, las3)
str(merged)



Point distance function

Description

calculates euclidean distances for n dimensions

Usage

p_dist(p1, p2)

Arguments

p1

point 1

p2

point 2

Value

the distance between the two points

Examples

p_dist(c(0,0), c(3,4))

Point distance function

Description

calculates euclidean distances for n dimensions between a matrix of points and a single point

Usage

p_mat_dist(mat, p)

Arguments

mat

matrix with points as rows

p

point to calculate distances

Value

the distances between every row of mat and p

Examples

p_mat_dist(as.matrix(cbind(runif(100),runif(100))), c(3,4))

Function to plot the inventory results into a lidR 3d plot of the point cloud

Description

Function to plot the inventory results into a lidR 3d plot of the point cloud

Usage

plot_inventory(plot, inventory, col = NA, cex = 1.5, label_col = "white")

Arguments

plot

lidR 3d plot

inventory

data.frame with the inventory results

col

color of the spheres

cex

numeric size of the labels

label_col

character color of the labels

Value

the plot with the inventory results

Examples


# read example data
file = system.file("extdata", "beech.las", package="CspStandSegmentation")
las = lidR::readTLSLAS(file)

# find tree positions as starting points for segmentation
map <- CspStandSegmentation::find_base_coordinates_raster(las)

# segment trees
segmented <- las |>
  CspStandSegmentation::add_geometry(n_cores = 2) |>
  CspStandSegmentation::csp_cost_segmentation(map, 1, N_cores = 2)

# perform inventory
inventory <- CspStandSegmentation::forest_inventory(segmented, n_cores = 2)

# plot the results
## Not run: 
x <- lidR::plot(segmented, color = "TreeID")
plot_inventory(x, inventory)

## End(Not run)



Returns the angle between the center of the circle and a point in degrees

Description

Returns the angle between the center of the circle and a point in degrees

Usage

point_center_angle(point, circle)

Arguments

point

numeric vector of length 2 c(X,Y)

circle

numeric vector of length 3 c(center_X, center_Y, radius)

Value

numeric angle in degrees


Helper function to compute distances from a point to the circle

Description

Helper function to compute distances from a point to the circle

Usage

point_circle_distance(point, circle)

Arguments

point

numeric vector of length 2 c(X,Y)

circle

numeric vector of length 3 c(center_X, center_Y, radius)

Value

numeric distance from the point to the circle


RANSAC circle fitting algorithm specially adapted for tree DBH estimation

Description

This function fits a circle to a set of points using the RANSAC algorithm it maximizes the points that are in the circle and the number of filled 36 degree angle segments Therefore, this function searches for the most complete circle with the highest number of points represented.

Usage

ransac_circle_fit(
  data,
  n_iterations = 1000,
  distance_threshold = 0.01,
  min_inliers = 3
)

Arguments

data

numeric matrix with 2 columns (X, Y) representing the point cloud

n_iterations

integer maximum number of iterations

distance_threshold

numeric maximum distance from a point to the circle to be considered an inlier

min_inliers

integer minimum number of inliers to consider the circle as valid

Value

a list with the following elements: circle: the center coordinates and radius of the circle inliers: number of points within the circles dist threshold angle_segs: number of populated 10deg angular segments of the circle using the distance_threshold n_iter: number of iterations run


Suppress only the cat() output

Description

Suppress only the cat() output

Usage

suppress_cat(f, ...)

Arguments

f

function to be called

...

parameters to the function

Value

the return value of the function


helper function to voxelize a las element

Description

Calculate voxel mean values for all numeric attributes in the las@data table including the XYZ-coordinates.

Usage

voxelize_points_mean_attributes(las, res)

Arguments

las

a lidR::LAS element

res

voxel resolution in meter

Value

a las element with XYZ-coordinates as the voxel center and X_gr,Y_gr,Z_gr as the center of gravity (mean point coordinates) as well as all other numeric columns voxel mean values with their original name.

Author(s)

Julian Frey <julian.frey@wwd.uni-freiburg.de>

See Also

voxelize_points

Examples


# read example data
file = system.file("extdata", "beech.las", package="CspStandSegmentation")
las = lidR::readTLSLAS(file)
vox <- las |> voxelize_points_mean_attributes(1)