| Title: | Tree Segmentation in MLS/TLS Point Clouds |
|---|---|
| Description: | Semantic segmentation, instance segmentaion and Quantitative Structure Model in MLS and TLS Point Clouds. |
| Authors: | Jean-Romain Roussel |
| Maintainer: | Jean-Romain Roussel <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 1.1.2 |
| Built: | 2026-10-13 23:42:59 UTC |
| Source: | https://github.com/r-lidar/arbor |
Subsets a 'qsf' object while preserving its class.
## S3 method for class 'qsf' x[i, ...]## S3 method for class 'qsf' x[i, ...]
x |
A 'qsf' object. |
i |
Index specifying the elements to extract. |
... |
Additional arguments passed to '['. |
A 'qsf' object containing the selected QSMs.
qsf ObjectsDetermines if elements of a qsf object exist within a las object.
## S4 method for signature 'LAS' x %in% table## S4 method for signature 'LAS' x %in% table
x |
An object of class |
table |
An object of class |
A logical vector indicating matching elements.
Adds a set of synthetic ground points below a single-tree point cloud. Useful to perform arbor operations on already externally isolated trees
add_single_tree_ground(las, n = 1000)add_single_tree_ground(las, n = 1000)
las |
A |
n |
integer. Number of ground points |
A LAS object containing the original points plus n synthetic
ground points.
Built-in variables. See the Arbor book for more details.
ARBORTREE ARBORLOW ARBORUNDERSTORY ARBORBUFFERARBORTREE ARBORLOW ARBORUNDERSTORY ARBORBUFFER
ARBORTREE is used in the UserData attributes. It flags points that are valid trees.
Usually, all points of interest are flagged as ARBORTREE.
ARBORLOW is used in the UserData attributes. It flags low points that are not processed by
Arbor at all.
ARBORUNDERSTORY is used in the UserData attributes. It flags small trees that are not
processed to build QSMs. See flag_small_trees.
ARBORBUFFER is used in the UserData attributes. It flags points in the buffer area. See
flag_buffer.
Show available allometric models
available_allometries()available_allometries()
A data.frame containing available models and their reference URLs:
Model name
DOI or reference link
available_allometries()available_allometries()
Extracts points belonging to trees surrounding a target tree, based on different spatial context definitions (contact, crown extent, or root zone). This is useful for analysing neighbourhood interactions, competition, or structural context around individual trees.
extract_tree_context(las, tree, exclude_tree = FALSE)extract_tree_context(las, tree, exclude_tree = FALSE)
las |
A |
tree |
Either a numeric tree ID corresponding to |
exclude_tree |
Logical. If |
A LAS object containing points from neighbouring trees that form
the selected context.
Other experimental:
qsf_segment_semantic()
## Not run: las <- readLAS("segmented_forest.las") tree <- lidR::filter_poi(las, treeID == 10) context <- extract_tree_context(las, tree, exclude_tree = TRUE) x <- plot_instance(context) plot(tree, add = x, pal = "red") ## End(Not run)## Not run: las <- readLAS("segmented_forest.las") tree <- lidR::filter_poi(las, treeID == 10) context <- extract_tree_context(las, tree, exclude_tree = TRUE) x <- plot_instance(context) plot(tree, add = x, pal = "red") ## End(Not run)
In order to perform instance segmentation with segment_instance we need some seeds with reference treeIDs. Thus function finds the seeds. See the Arbor book for mode details.
find_seeds(las, params = arbor_parameters_default)find_seeds(las, params = arbor_parameters_default)
las |
A LAS object from lidR. |
params |
list See parameters. |
Utilities to flag trees that should be excluded from downstream processing.
These functions assign values to the UserData attribute of the point cloud in order to later
filter data on the fly. See the Arbor book
flag_small_trees(las, max_height = 2) flag_buffer(las, seeds, buffer = -5)flag_small_trees(las, max_height = 2) flag_buffer(las, seeds, buffer = -5)
las |
A LAS object from lidR containing segmented trees. |
max_height |
Numeric. Trees with a height lower than this threshold (in meters) are flagged. |
seeds |
A LAS object containing tree seeds, typically generated with find_seeds. If missing, an internal routine estimates seed positions from the lowest point of each tree. |
buffer |
Numeric value (in meters) used to shrink the convex hull before
filtering trees. Default is |
flag_small_trees() flags trees below a given height threshold, which are often
poorly segmented or correspond to understory vegetation.flag_buffer() flags trees located near the edges of a point cloud by clipping
trees whose seed points fall outside a buffered polygon.
See the Arbor book for more details.
A modified LAS object with flagged trees assigned negative treeID values.
Color palette for semantic segmentation plot
foliage.colorsfoliage.colors
Homogenization of the point cloud using a hybrid approach that includes Barycentric Voxel Decimation and reinjection of random points. See the Arbor book for mode details.
hybrid_homogeneization(las, res = 0.02)hybrid_homogeneization(las, res = 0.02)
las |
LAS object from lidR |
res |
Voxel resolution. |
Install the required files to be able to run some simple arbor commands from a terminal.
install_cmd_tools()install_cmd_tools()
The built-in internal variable 'arbor_parameters_default' contains all the parameters used by the arbor functions. The variable itself is a nested list that is intentionally not documented. There are numerous parameters controlling thresholds, neighborhoods, clustering, noise filtering, and pathfinder configuration. If users had to adjust and understand all of them, the software would quickly become unusable. While every parameter can be configured, we deliberately avoid documenting them to prevent endless tweaking. The pipeline MUST work reliably out of the box with the default parameters. See the [Arbor book](https://r-lidar.github.io/arbor_book/)
arbor_parameters_defaultarbor_parameters_default
params = arbor_parameters_default params$global$cut_above_ground <- 0.25params = arbor_parameters_default params$global$cut_above_ground <- 0.25
Plots the QSM data as a series of connected cylinders, with color-coding based on branch attributes.
'colorize_trees' assigns a RGB value per tree for rendering in third party softwares.
## S3 method for class 'qsf' plot(x, ..., color = "branch_order", pal = "auto", add = NULL) ## S3 method for class 'qsm' plot(x, ...) plot_qsm( x, add = NULL, color = "branch_order", skeleton = TRUE, cylinder = TRUE, pal = "auto", ... ) plot_likelihood(las, dtm = NULL, ...) plot_semantic(las, dtm = NULL, ...) plot_instance(las, dtm = NULL, ...) plot_semantic_instance(las, dtm = NULL, ...) plot_passage(las, dtm = NULL, th = 0, ...) colorize_trees(las, darken_foliage = TRUE)## S3 method for class 'qsf' plot(x, ..., color = "branch_order", pal = "auto", add = NULL) ## S3 method for class 'qsm' plot(x, ...) plot_qsm( x, add = NULL, color = "branch_order", skeleton = TRUE, cylinder = TRUE, pal = "auto", ... ) plot_likelihood(las, dtm = NULL, ...) plot_semantic(las, dtm = NULL, ...) plot_instance(las, dtm = NULL, ...) plot_semantic_instance(las, dtm = NULL, ...) plot_passage(las, dtm = NULL, th = 0, ...) colorize_trees(las, darken_foliage = TRUE)
x |
A QSM of QSF |
... |
propagated to lidR::plot |
color |
The attribute for color mapping. |
pal |
Color palette |
add |
A numeric vector for translation offsets. Like in the lidR package. |
skeleton, cylinder
|
boolean. plot the skeleton or the cylinders or both. |
las |
LAS object |
dtm |
SpatRaster DTM |
th |
threshold to remove points with few passages |
darken_foliage |
boolean. In addition to one color per tree, the foliage is darkened the same color that its corresponding tree to display semantic and instance segmentation in one rendering. |
qsm |
A QSM |
Displays a short summary of a Quantitative Structure Forest followed by
the standard data.table print output.
## S3 method for class 'qsf' print(x, ...)## S3 method for class 'qsf' print(x, ...)
x |
A |
... |
Unused (for S3 compatibility). |
Invisibly returns x.
Displays a short summary of a Quantitative Structure Model followed by
the standard data.table print output.
## S3 method for class 'qsm' print(x, ...)## S3 method for class 'qsm' print(x, ...)
x |
A |
... |
Unused (for S3 compatibility). |
Invisibly returns x.
Batch processing of QSM models with parallel execution. Trees that are flagged with flag_buffer or flag_small_trees are excluded from the computation. See also the Arbor book for more details.
qsf(las, min_height = 2, params = arbor_parameters_default)qsf(las, min_height = 2, params = arbor_parameters_default)
las |
A point cloud with semantic and instance segmentation computed. |
min_height |
numeric. Default: any instance higher than 2 m generates a QSM. This is pretty equivalent to flag_small_trees, but forces a height limit even if the user did not call flag_small_trees, thus ensuring that no QSM lower than 2 m is computed. |
params |
list. See parameters. |
A qsf object.
qsf_filter() subsets a qsf object, retaining only the trees that
satisfy the requested criteria. Filters compose with AND semantics: a tree
is retained only if it satisfies every criterion supplied.
A handful of qsf_filter_*() wrappers cover the most common triage needs
on top of qsf_filter(): pulling out clean ("ok") trees, trees carrying
any warning, error or message, or (with qsf_filter_flagged()) anything
with a log entry at all. Each one still takes ..., so it composes with
the other qsf_filter() criteria (dbh, height, ...) in a single
call. See details section for more in depth details
qsf_filter( qsf, code = NULL, type = NULL, invert = FALSE, flag = NULL, dbh = NULL, height = NULL, ... ) qsf_filter_ok(qsf, ...) qsf_filter_sapling(qsf, ...) qsf_filter_nomeasure(qsf, ...) qsf_filter_broken(qsf, ...) qsf_filter_flagged(qsf, code = NULL, type = NULL, ...) qsf_filter_warnings(qsf, code = NULL, ...) qsf_filter_errors(qsf, code = NULL, ...) qsf_filter_messages(qsf, code = NULL, ...)qsf_filter( qsf, code = NULL, type = NULL, invert = FALSE, flag = NULL, dbh = NULL, height = NULL, ... ) qsf_filter_ok(qsf, ...) qsf_filter_sapling(qsf, ...) qsf_filter_nomeasure(qsf, ...) qsf_filter_broken(qsf, ...) qsf_filter_flagged(qsf, code = NULL, type = NULL, ...) qsf_filter_warnings(qsf, code = NULL, ...) qsf_filter_errors(qsf, code = NULL, ...) qsf_filter_messages(qsf, code = NULL, ...)
qsf |
A |
code |
character. One or more qsf_log codes (e.g. |
type |
character. One or more log types: |
invert |
logical. If |
flag |
logical. Filters on the mere presence of a log entry, regardless
of its code or type (independent of |
dbh |
numeric(2). Keep only trees with a DBH (see qsm_dbh) inside
|
height |
numeric(2). Keep only trees with a height (see qsm_height)
inside |
... |
In |
qsf_filter_ok() retains QSMs with no log entry at all. They are assumed to be
good QSMs.
qsf_filter_sapling() retains QSMs for which no measurements were performed
according to the height allometry rules (see Arbor Book).
Under Arbor's definition, they are considered saplings.
qsf_filter_nomeasure() retains QSMs for which no measurements were found because
the QSM algorithm failed to measure anything. However, these trees are not saplings.
Saplings are different. For sapling the algortihm does not even try, on purpose.
qsf_filter_broken() retains QSMs for which Arbor automatically detected
that they correspond to broken trees.
qsf_filter_flagged() retains QSMs with at least one log entry, of any kind.
qsf_filter_warnings() retains QSMs carrying at least one warning.
qsf_filter_errors() retains QSMs carrying at least one error.
qsf_filter_messages() retains QSMs carrying at least one plain message.
A qsf object containing only the selected QSMs.
f <- system.file("extdata", "oak-plantation.qsf", package="arbor") qsf <- qsf_read(f) # qsf_filter_*(): quick selectors qsf_filter_ok(qsf) # trees with no log entry at all qsf_filter_sapling(qsf) # saplings according to arbor's definition qsf_filter_flagged(qsf) # trees with at least one log entry, any kind qsf_filter_warnings(qsf) # trees carrying at least one warning qsf_filter_warnings(qsf, "W2") # ... specifically warning code W2 qsf_filter_messages(qsf) # trees carrying at least one plain message # qsf_filter(): the general-purpose filter # Keep only trees flagged with code W0 or W3 qsf_filter(qsf, code = c("W0", "W3")) # Exclude trees flagged with code W2 or W3, while keeping trees with DBH > 20 cm qsf_filter(qsf, code = c("W2", "W3"), invert = TRUE, dbh = c(0.2, Inf)) # Trees with no log entry at all ("good" QSMs) qsf_filter(qsf, flag = FALSE) # Trees with at least one log entry, of any kind qsf_filter(qsf, flag = TRUE)f <- system.file("extdata", "oak-plantation.qsf", package="arbor") qsf <- qsf_read(f) # qsf_filter_*(): quick selectors qsf_filter_ok(qsf) # trees with no log entry at all qsf_filter_sapling(qsf) # saplings according to arbor's definition qsf_filter_flagged(qsf) # trees with at least one log entry, any kind qsf_filter_warnings(qsf) # trees carrying at least one warning qsf_filter_warnings(qsf, "W2") # ... specifically warning code W2 qsf_filter_messages(qsf) # trees carrying at least one plain message # qsf_filter(): the general-purpose filter # Keep only trees flagged with code W0 or W3 qsf_filter(qsf, code = c("W0", "W3")) # Exclude trees flagged with code W2 or W3, while keeping trees with DBH > 20 cm qsf_filter(qsf, code = c("W2", "W3"), invert = TRUE, dbh = c(0.2, Inf)) # Trees with no log entry at all ("good" QSMs) qsf_filter(qsf, flag = FALSE) # Trees with at least one log entry, of any kind qsf_filter(qsf, flag = TRUE)
Use qsf_log after qsf to get a structured summary of the log messages recorded during the QSM reconstruction of every tree in a 'qsf' object.
qsf_log(qsf) ## S3 method for class 'qsf_log' print(x, ...)qsf_log(qsf) ## S3 method for class 'qsf_log' print(x, ...)
qsf |
qsf |
x |
A 'qsf_log' object, as returned by qsf_log. |
... |
Unused (for S3 compatibility). |
A named list, one element per distinct log entry. Elements are named by their 'code' (e.g. '"W0"'). Each element is a list with:
'code': the log code (e.g. '"W0"'), or 'NA' for legacy, uncoded messages.
'type': '"warning"', '"message"', '"error"', or 'NA' if unknown.
'label': the short, human readable label extracted from the message.
'message': an example of the full message text.
'index': the indices, within 'qsf', of the trees carrying this log entry.
'treeID': the tree IDs of the trees carrying this log entry.
Messages recorded on a QSM start with a code '[<L><n>]', where '<L>' is one of 'W' (warning), 'M' (message) or 'E' (error) and '<n>' is a numeric identifier, immediately followed by '[Short label]' and the free text explanation, e.g. '"[W0] [No wood point] This tree has no point labelled as wood"'. This is fully backward compatible: messages recorded before this convention was introduced have no code ('code = NA') and are grouped by their short label instead, exactly as before.
f <- system.file("extdata", "oak-plantation.qsf", package="arbor") qsf <- qsf_read(f) logs <- qsf_log(qsf) print(logs) logs$W2 logs$W2$treeID logs$W3f <- system.file("extdata", "oak-plantation.qsf", package="arbor") qsf <- qsf_read(f) logs <- qsf_log(qsf) print(logs) logs$W2 logs$W2$treeID logs$W3
Reads a .qsf manifest file produced by qsf_write (whenever
"qsm" is among the requested formats) and reconstructs the
Quantitative Structural Forest (QSF) it describes.
qsf_read(path)qsf_read(path)
path |
A string giving the path to the |
A .qsf file is a lightweight, standalone manifest that indexes a
collection of .qsm files by relative path - conceptually similar to
a virtual raster mosaic (VRT). Currently only "virtual" manifests are
supported: the manifest itself holds no tree data, only relative
references to the .qsm files sitting next to it, which are read in
turn.
A qsf object, i.e. a list of qsm objects.
f <- system.file("extdata", "oak-plantation.qsf", package="arbor") oak <- qsf_read(f) ## Not run: plot(oak) ## End(Not run)f <- system.file("extdata", "oak-plantation.qsf", package="arbor") oak <- qsf_read(f) ## Not run: plot(oak) ## End(Not run)
Performs an experimental semantic segmentation of a single-tree point cloud using distances derived from a Quantitative Structure Model (QSM). Points are labelled as foliage or non-foliage based on their proximity to QSM elements.
qsf_segment_semantic(las, qsf)qsf_segment_semantic(las, qsf)
las |
A |
qsf |
A Quantitative Structure Forest associated with |
Other experimental:
extract_tree_context()
Extracts global statistics from each Quantitative Structure Model (QSM) contained in a Quantitative Structure Forest (QSF) object and returns them as an sf point object. Each point corresponds to the root position of an individual tree.
qsf_treemap(qsf)qsf_treemap(qsf)
qsf |
A Quantitative Structure Forest (QSF) |
An sf object with one feature per QSM. Attributes correspond
to global statistics returned by qsm_stats, and geometry is
defined by the root coordinates (X_root, Y_root, Z_root).
Writes a Quantitative Structure Forest (QSF), i.e. a collection of QSMs to files in QSM, OBJ, PLY, STL, CSV, or TXT format.
qsf_write(qsf, path, format = c("qsm", "obj"), binary = TRUE)qsf_write(qsf, path, format = c("qsm", "obj"), binary = TRUE)
qsf |
A QSF object to be written. |
path |
A string giving the output directory path (directory mode), or a
file path ending in |
format |
A character vector specifying the export format(s) (e.g., |
binary |
Logical. Indicates whether to output binary or ASCII where supported.
Defaults to |
The function operates in two distinct modes:
Directory mode (recommended): If path is a directory (or has
no file extension), each QSM in the forest is written into its own separate
file inside path/format/, named after the QSM's name (or its id if it has
no name). Multiple formats can be exported at once by passing several values
in format. Every tree is always fully separate and identifiable in this
mode, for every format, because each tree gets its own file.
Single-file mode: If path points to a specific file path
(i.e., its extension is non-empty), a single file is written containing all
QSMs together. The file format is inferred strictly from the file
extension of path, and the format argument is ignored. This is only
supported for the obj, ply, and stl mesh formats.
qsm, csv, and txt do not support combining several
trees into one file and will raise an error if used this way. Even among the
supported formats, how well individual trees stay distinguishable inside the
combined file varies a lot (see "Limits of single-file mode" below), so this
mode is best reserved for cases where you specifically need one file to hand
to some other piece of software (e.g. importing a whole forest into Blender
in one go).
Supported formats:
.qsm: Native binary format. Directory mode only. Also produces
a sibling .qsf manifest file that indexes every .qsm file
written, by relative path (see qsf_read).
.obj, .ply, or .stl: Mesh formats. Available in
both directory and single-file mode.
.csv or .txt: ASCII table formats. Directory mode only.
Returns TRUE invisibly upon completion.
When several QSMs are combined into one obj, ply, or stl
file, each format has a different (and sometimes very limited) way of keeping
individual trees identifiable:
OBJ: Full support. Each QSM becomes its own named o object
inside the file. This is NOT respected by all OBJ readers. E.g. Blender
respects the OBJ specifications and load individual trees but not
CloudCompare that loads one massive undifferentiated mesh.
PLY: Partial support. All QSMs share one vertex/face list (a
single mesh), but every face carries a qsm_id integer property, and the
header records one comment per QSM with its id and name. Software that lets
you inspect or filter by custom face properties can recover the original
trees; a generic PLY viewer will just show one undifferentiated mesh.
STL (ASCII, binary = FALSE): Weak, non-standard support.
Each QSM is written as its own solid <name> ... endsolid <name> block.
This is a common convention, not part of the STL standard, so support is
inconsistent: some tools import every block as a separate named object,
others only read the first block, and others merge everything into one
mesh. Always test with your target software before relying on this.
STL (binary, binary = TRUE, the default): No support at
all. Binary STL has no concept of an object name or grouping - it is just a
flat, unlabelled list of triangles. Combining multiple QSMs into one binary
STL file merges them into a single anonymous mesh with no way to tell which
triangle came from which tree. Use binary = FALSE (ASCII STL), or
switch to obj or ply, if keeping trees separate matters.
## Not run: # Single-file mode: maybe the simplest use case. Writes forest.qsf file that references # the .qsm files in the subdirectory "qsm/". In this case it does produces numerous # files because .qsf file format only contains metadata. qsf_write(qsf, "forest.qsf") # Directory mode: one file per tree. # Writes forest/qsm/<name>.qsm and forest/obj/<name>.obj for every tree. # Writes also path/to/directory/directory.qsf to reference all the .qsm file qsf_write(qsf, "path/to/directory/", format = c("qsm", "obj")) # Directory mode: several formats at once, ASCII where applicable. qsf_write(qsf, "forest", format = c("ply", "csv"), binary = FALSE) # Single-file mode: with OBJ format is the safest choice. Write a single OBJ file. Every # tree keeps its name as a separate 'o' object. Be careful that NOT all software will # import as such. Blender supports it not CloudCompare. qsf_write(qsf, "forest_all_trees.obj") # Single-file mode: with PLY format. One mesh, but trees can still be told apart via the # per-face 'qsm_id' property if your software can filter on it. qsf_write(qsf, "forest_all_trees.ply") ## End(Not run)## Not run: # Single-file mode: maybe the simplest use case. Writes forest.qsf file that references # the .qsm files in the subdirectory "qsm/". In this case it does produces numerous # files because .qsf file format only contains metadata. qsf_write(qsf, "forest.qsf") # Directory mode: one file per tree. # Writes forest/qsm/<name>.qsm and forest/obj/<name>.obj for every tree. # Writes also path/to/directory/directory.qsf to reference all the .qsm file qsf_write(qsf, "path/to/directory/", format = c("qsm", "obj")) # Directory mode: several formats at once, ASCII where applicable. qsf_write(qsf, "forest", format = c("ply", "csv"), binary = FALSE) # Single-file mode: with OBJ format is the safest choice. Write a single OBJ file. Every # tree keeps its name as a separate 'o' object. Be careful that NOT all software will # import as such. Blender supports it not CloudCompare. qsf_write(qsf, "forest_all_trees.obj") # Single-file mode: with PLY format. One mesh, but trees can still be told apart via the # per-face 'qsm_id' property if your software can filter on it. qsf_write(qsf, "forest_all_trees.ply") ## End(Not run)
This function processes a tree point cloud to generate a Quantitative Structure Model (QSM) See the Arbor book for mode details.
qsm(tree, params = arbor_parameters_default)qsm(tree, params = arbor_parameters_default)
tree |
A |
params |
list See parameters. |
qsm_write qsm_read qsm_dbh qsm_stats
f <- system.file("extdata", "tree_qsm.laz", package="arbor") tree <- lidR::readLAS(f) qsm = qsm(tree) ## Not run: x = plot_semantic(tree) plot_qsm(qsm, add = x, color = "branch_order", cylinder = TRUE) ## End(Not run)f <- system.file("extdata", "tree_qsm.laz", package="arbor") tree <- lidR::readLAS(f) qsm = qsm(tree) ## Not run: x = plot_semantic(tree) plot_qsm(qsm, add = x, color = "branch_order", cylinder = TRUE) ## End(Not run)
Extract the diameter at breast height (DBH) from a Quantitative Structure Model (QSM).
qsm_dbh(qs, bh = 1.3) add_dbh3d(x, df, col = "red", lwd = 3)qsm_dbh(qs, bh = 1.3) add_dbh3d(x, df, col = "red", lwd = 3)
qs |
A QSM or QSF |
bh |
Numeric. Breast height (in meters). Default is 1.30. |
x |
An alignment offset returned by a 'plot' function. |
df |
the output of 'qsm_dbh()' |
col, lwd
|
render parameters |
A data.frame with:
dbh: DBH in meter
pos: xyz position of the center
normal: normal vector of the circle
f <- system.file("extdata", "tree_qsm.laz", package="arbor") tree <- lidR::readLAS(f) qsm <- qsm(tree) ans <- qsm_dbh(qsm) ## Not run: plot_semantic(tree) |> add_dbh3d(ans) plot_qsm(qsm) |> add_dbh3d(ans) ## End(Not run)f <- system.file("extdata", "tree_qsm.laz", package="arbor") tree <- lidR::readLAS(f) qsm <- qsm(tree) ans <- qsm_dbh(qsm) ## Not run: plot_semantic(tree) |> add_dbh3d(ans) plot_qsm(qsm) |> add_dbh3d(ans) ## End(Not run)
Loads QSM segment data from a CSV or QSM files.
qsm_read(x)qsm_read(x)
x |
A character string specifying the file path. |
A data frame of QSM segment data.
Computes structural statistics from a Quantitative Structure Model (QSM), including volume distributions by radius and branch order, stem taper, and global tree metrics such as DBH, height, and total volume. Optionally, statistics can be complemented with information derived from a LAS point cloud of the tree and visual diagnostics can be displayed.
qsm_stats(qs, ..., display = FALSE)qsm_stats(qs, ..., display = FALSE)
qs |
A QSM or QSF produced by qsm or qsf. If 'qs' is a 'QSF' the output is less comprehensive to fit in a 'data.frame' |
... |
passed to qsm_dbh. |
display |
Logical. If 'TRUE', diagnostic plots are displayed, including volume distributions, stem profile, and vertical projections. |
The function computes:
Volume and cylinder counts by radius class (2 cm bins).
Volume distribution and cumulative percentage by branch order.
Stem taper profile from the main axis.
Global tree statistics such as DBH, total volume, and height.
When a 'LAS' object is supplied, metrics are derived directly from the point cloud and QSM rather than the QSM alone.
A named list with the following elements:
stats_by_order: A 'data.table' with volume and cumulative percentage by branch order.
stats_by_radius: A 'data.table' with total volume and cylinder count by radius class.
stats_global: A 'data.frame' containing global tree metrics (coordinates, DBH, volume, height).
stem_taper: A 'data.frame' describing stem diameter as a function of distance to root.
f <- system.file("extdata", "tree_qsm.laz", package="arbor") tree <- lidR::readLAS(f) qsm <- qsm(tree) ans <- qsm_stats(qsm, display = TRUE)f <- system.file("extdata", "tree_qsm.laz", package="arbor") tree <- lidR::readLAS(f) qsm <- qsm(tree) ans <- qsm_stats(qsm, display = TRUE)
Subset a QSM or QSF retaining only the stem, the merchantable or other options.qsm_stem() retains only the stem of the QSM (branching_order = 1). Applying the function
to a QSF is like a loop on each QSM.qsm_merchantable() retains only the merchantable parts of the QSM (radius > merchantable_radius
with additional graph validity constrains). Applying the function to a QSF is like a loop on each QSM.qsf_merchantable() retains only the merchantable trees of the QSF (DBH > merchantable_radius). It
is not equivalent to qsm_merchantable() as it does not subset the QSMs, it subsets the QSF.
The entire trees are retains.
qsm_stem(qs) qsm_merchantable(qs, merchantable_radius = 0.045, merchantable_length = 1) qsf_merchantable(qsf, merchantable_radius = 0.045) qsm_nostump(qs, stump_height = 0.15)qsm_stem(qs) qsm_merchantable(qs, merchantable_radius = 0.045, merchantable_length = 1) qsf_merchantable(qsf, merchantable_radius = 0.045) qsm_nostump(qs, stump_height = 0.15)
qs |
A QSM or QSF |
merchantable_radius |
The radius considered merchantable. Default 4.5 cm (9 cm diameter) which is the standard in Canada. |
merchantable_length |
The length considered merchantable. Default 1 m which is the standard in Canada. |
qsf |
A QSF |
stump_height |
The height to remove from the root. Default 15 cm |
Simple tools for QSM.
qsm_volume(qs) qsm_height(qs) qsm_message(qsm, short = FALSE) qsm_treeid(qs) ## S3 method for class 'qsm' qsm_treeid(qs) ## S3 method for class 'qsf' qsm_treeid(qs)qsm_volume(qs) qsm_height(qs) qsm_message(qsm, short = FALSE) qsm_treeid(qs) ## S3 method for class 'qsm' qsm_treeid(qs) ## S3 method for class 'qsf' qsm_treeid(qs)
qs |
A QSM or QSF |
qsm |
A QSM |
short |
bool. Shorter message: only the human readable label is returned (the optional leading '[<code>]' tag, see qsf_log, is stripped as well). |
Writes a Quantitative Structure Model (QSM) to a file in either OBJ, PLY or CSV format, based on the file extension.
qsm_write(qsm, file, binary = TRUE)qsm_write(qsm, file, binary = TRUE)
qsm |
A QSM object to be written. |
file |
A string giving the path to the output file. The file extension determines the format (e.g., ".qsm", ".ply", "obj", ".csv", ".txt", ".stl"). |
binary |
Boolean. Used if the format supports ASCII or binary |
Supported formats:
.qsm: writes the QSM as native binary format
.ply or .obj or .stl: writes the QSM as a mesh file
.csv or .txt: writes the QSM as ASCII table
## Not run: qsm_write(qsm, "tree.qsm") qsm_write(qsm, "tree.obj") qsm_write(qsm, "tree.csv") ## End(Not run)## Not run: qsm_write(qsm, "tree.qsm") qsm_write(qsm, "tree.obj") qsm_write(qsm, "tree.csv") ## End(Not run)
Detects and merges tree instances that were split into multiple treeIDs
during segmentation (over-segmentation) and merges them back into single,
consistent instances. This module is already executed by segment_instance.
Over-segmentation is detected by scanning the point cloud layer by layer
across a range of height-above-ground (HAG) values. Within each layer,
points are clustered (DBSCAN) and a circular stem cross-section is fitted
to each cluster. When a well-fitted circle's inlier points are supported
by more than one treeID, this is evidence that two (or more)
instances actually belong to the same stem. Evidence is accumulated
across all layers into a matching table of (reference, candidate) treeID
pairs, each backed by a count of how many circles support the pair and
how many inlier points came from each side. Chains of matches (e.g.
1-2, 2-3) are resolved with a union-find so that every id in a chain
collapses onto a single canonical id.
resolve_oversegmentation(las)resolve_oversegmentation(las)
las |
A |
The input LAS object, with treeID values remapped
wherever over-segmentation was detected and merged. Instances that were
not found to be over-segmented are left untouched.
Classifies ground points and computes height above ground. See the Arbor book for mode details.
segment_ground(las, params = arbor_parameters_default)segment_ground(las, params = arbor_parameters_default)
las |
A |
params |
list See parameters. |
find_seeds, segment_semantic, segment_instance
This function performs individual tree instance segmentation by constructing a point-based network
and computing the least-cost path for each point to predefined seed points. The goal is to assign
each point in the point cloud a treeID corresponding to its nearest seed, based on the least-cost
path computed through the network. The process incorporates both spatial and heuristic constraints
to segment trees effectively. See the Arbor book for mode details.
segment_instance(las, seeds, params = arbor_parameters_default)segment_instance(las, seeds, params = arbor_parameters_default)
las |
A |
seeds |
An |
params |
list See parameters. |
A LAS object with an additional attribute treeID, indicating the ID of the tree to
which each point belongs.
This function performs wood-foliage semantic segmentation by constructing a network of points
using k-nearest neighbors (KNN). A pathfinding algorithm navigates this network to identify the
tree skeleton by finding the least-cost path from every point in space to the ground. Each point
is classified as wood or foliage based on two main criteria: (1) Proximity to the detected skeleton and
(2) wood likelihood exceeding a given threshold. The function wood_likelihood must be applied
first. The point cloud must have an attribute 'hag' (see height_above_ground)
Finally, a connected component step removes small clusters incorrectly classified as wood.
See the Arbor book for mode details.
segment_semantic(las, params = arbor_parameters_default)segment_semantic(las, params = arbor_parameters_default)
las |
A LAS object. |
params |
list See parameters. |
A LAS object with an additional attribute foliage. 0 - wood, 1 and 2 foliage. The UserData
attribute is also assigned two constant: ARBORLOW for points below the cut threshold ans ARBORTREE
for all others points
Get or set the projection of a QSM and QSF objects
## S3 method for class 'qsm' st_crs(x, ...) ## S3 method for class 'qsf' st_crs(x, ...) ## S3 replacement method for class 'qsm' st_crs(x) <- value ## S3 replacement method for class 'qsf' st_crs(x) <- value## S3 method for class 'qsm' st_crs(x, ...) ## S3 method for class 'qsf' st_crs(x, ...) ## S3 replacement method for class 'qsm' st_crs(x) <- value ## S3 replacement method for class 'qsf' st_crs(x) <- value
x |
a QSM or QSF |
... |
Unused. |
value |
see sf::st_crs |
These functions implement a simple workflow to:
read trajectory file,
compute point-to-sensor range using the trajectory,
filter points based on range and height above ground.
They are designed to be used together, but can also be called independently.
read_trajectory: Reads a trajectory file and converts it to an sf point object.
The first column is assumed to be the GPS time and is renamed to gpstime.
The file must contain columns named x, y, and z.
add_range: Computes the distance between each point and the sensor position
derived from a trajectory, and stores it as a new LAS attribute named
range.
filter_range: Filters a point cloud by removing points that are too far to the sensor
and below a given height above ground. For example, if distance = 15
and hag_max = 4, points that are beyond 15 m of the sensor and
lower than 4 m above ground are removed. Points farther than 15 m from the
sensor but higher than 4 m above ground, are retained. This prevents the
removal of canopy point.
read_trajectory(file, dt = 0.5) add_range(las, traj) filter_range(las, distance = 15, hag_max = 4)read_trajectory(file, dt = 0.5) add_range(las, traj) filter_range(las, distance = 15, hag_max = 4)
file |
Character. Path to the ASCII trajectory file |
dt |
Numeric. Keep a record every |
las |
A |
traj |
An |
distance |
Numeric. Maximum allowed distance to the sensor. |
hag_max |
Numeric. Maximum allowed height above ground. |
An sf object with point geometry and a gpstime attribute.
A LAS object with an additional attribute range.
A filtered LAS object.
Transfers one or more attributes from a decimated (sparser) point cloud to a denser point cloud using a 1-nearest neighbor (1-NN) approach. Each point in the target point cloud receives the attribute value of its nearest neighbor in the source point cloud.
transfer_attributes(from, to, attributes)transfer_attributes(from, to, attributes)
from |
A 'LAS' object representing the source (typically decimated) point cloud containing the attributes to transfer. |
to |
A 'LAS' object representing the target (typically denser) point cloud that will receive the transferred attributes. |
attributes |
A character vector specifying the names of the attributes to transfer from 'from' to 'to'. |
The function performs a 1-NN search between the 'from' and 'to' point clouds using 'lidR::knnx()'. For each attribute listed in 'attributes', the values are assigned from the nearest point in 'from'. If available, the function also propagates the corresponding attribute description from the Extra Bytes metadata ('VLR$Extra_Bytes').
A 'LAS' object identical to 'to', but with the requested attributes added.
[lidR::knnx()], [lidR::add_lasattribute()]
## Not run: las <- transfer_attributes(las_decimated, las, attributes = "treeID", "foliage") ## End(Not run)## Not run: las <- transfer_attributes(las_decimated, las, attributes = "treeID", "foliage") ## End(Not run)
This function calculates the local anisotropy of each point in a LAS object using a k-nearest neighbors (KNN) approach. See the [Arbor book](https://r-lidar.github.io/arbor_book/) for mode details.
wood_likelihood(las, params = arbor_parameters_default)wood_likelihood(las, params = arbor_parameters_default)
las |
A LAS object from lidR. |
params |
list See parameters. |