knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
library(ITSMe)
The goal of the ITSMe (Individual Tree Structural Metrics) R package is to provide easy to use functions to quickly obtain structural metrics from individual tree point clouds and their respective TreeQSMs.
An overview of the basic structural metrics which can be measured from an individual tree point cloud with the ITSMe package:
| structural metric | function name |
| ------------------------------|:---------------------------------:|
| diameter at breast height (m) | dbh_pc
|
| diameter above buttresses (m) | dab_pc
|
| tree height (m) | tree_height_pc
|
| projected area (m$^{2}$) | projected_area_pc
|
| alpha volume (m$^{3}$) | alpha_volume_pc
|
The tree point cloud based functions were developed for point clouds acquired with TLS. But they can also be used for tree point clouds obtained from other LiDAR platforms (e.g. MLS, UAV-LS). Keep in mind that the accuracy of the metric measurements will depend on the quality of the data.
Make sure outliers and points that don't belong to the tree (especially near the trunk) are removed. Good segmentation quality leads to reliable measurements. Down sampling of the point cloud is not required but can reduce computation time for very big tree point clouds (down sampling to 2 cm is suggested).
Before running the functions on multiple tree point clouds at a time, it is advised to get familiar with the functions and their input parameters by running the functions on a single tree point cloud.
All of the point cloud based functions (mentioned above) need a tree point cloud
in the form of a data.frame with columns X, Y, Z as their first argument. The
ITSMe package provides a function read_tree_pc
that takes the path to a tree
point cloud file (txt, ply or las) as an argument and returns a data.frame with
X, Y, Z columns.
# Read the point cloud file from its' specified path tree_pc <- read_tree_pc(path = "path/to/point/cloud.txt")
The position of the tree can be easily determined using tree_position_pc
,
which determines the mean X, Y and Z value of the 100 lowest points of the tree
point cloud. Using the 100 lowest points makes it possible to use this function
also for UAV-LS tree point clouds that do not sample the stem very well.
# Measure tree position from the tree point cloud XYZ_pos <- tree_position_pc(pc = tree_pc)
The tree height can be measured using tree_height_pc
, which calculates the
difference between the Z-value of the highest and lowest point of the tree point
cloud. tree_height_pc
only requires the tree point cloud as an input. It also
optionally plots the tree point cloud in 2D (XZ and YZ plots next to each) when
the "plot" parameter equals TRUE.
# Measure tree height from the tree point cloud H <- tree_height_pc(pc = tree_pc) # also plot the tree point cloud H_out <- tree_height_pc(pc = tree_pc, plot = TRUE) H <- H_out$h plot_tree <- H_out$plot
However, for tree point clouds that are obtained with for example UAV-LS and
that do not sample the base of the tree sufficiently the tree height would be
underestimated. In this case a digital terrain model (DTM) with a certain
resolution can be provided to the tree_height_pc
function to improve tree
height estimation. The DTM also needs to be supplied in the data.frame format
with columns X, Y, and Z.
# Read the DTM DTM <- read_tree_pc(path = "path/to/dtm.txt") # Measure tree height from the tree point cloud and DTM with resolution r H <- tree_height_pc(pc = tree_pc, dtm = DTM, r = 1.5) # also plot the tree point cloud and estimated lowest point based on DTM H_out <- tree_height_pc(pc = tree_pc, dtm = DTM, r = 1.5, plot = TRUE) H <- H_out$h plot_tree <- H_out$plot
The provided resolution (r) value should be at least the resolution of the
provided DTM.
Depending on if your trees are buttressed or not, you will make a choice between
the diameter at breast height (DBH) and the diameter above buttresses (DAB). If
you are not sure if this is the case for your tree, you can check the slice
(and circle fitting) at 1.3 m with the diameter_slice_pc
function. The
diameter is measured as the circle fit to the slice points. But this function
also determines the functional diameter which is calculated as the diameter of
the circle with the same area as the concave hull (concavity = 4) fitted to the
slice points. This value could be more interesting for trees that inherently
have non-circular stems. But this metric is influenced more by point cloud and
segmentation quality (e.g. noisy points, or vegetation points which have not
been removed from the point cloud).
# Measure DBH from the tree point cloud and plot the circle fitting D_out <- diameter_slice_pc( pc = tree_pc, slice_height = 1.3, slice_thickness = 0.06, plot = TRUE ) diameter <- D_out$diameter functional_diameter <- D_out$fdiameter
For a regular non-buttressed tree:
For a buttressed tree:
The DBH is measured using dbh_pc
and based on the diameter_slice_pc
function
with slice_height of 1.3 m and a chosen slice_thickness (default = 0.06 m).
If branch or vegetation points are present in the slice, the lower (till 1.5 m
above lowest point) trunk is extracted (so without branch/vegetation points) and
the fit is done again. The dbh_pc
function needs the tree point cloud as an
input. If the argument plot is set TRUE it also plots the circle fitting. This
function also reports the functional DBH (fDBH).
# Measure DBH from the tree point cloud and plot the circle fitting DBH_out <- dbh_pc(pc = tree_pc, plot = TRUE) DBH <- DBH_out$dbh
Generally running the dbh_pc
with default values is fine. But there are two
other parameters that can be optimised which influence the trunk extraction. The
thresholdR2 parameter (default = 0.001) determines when the trunk extraction
is done. The trunk is extracted when the initial diameter estimation at breast
height results in NaN, a value higher than 2 m, the residuals are higher than
the thresholdR2 times the estimated diameter or the estimated diameter is
smaller than the diameter estimated at 0.15 m. For trees with non-circular stems
this value should be increased. Also for smaller trees (DBH < 10 cm) or tree
point cloud with a sub-optimal co-registration of scan positions, for example
due to wind) a higher value will lead to better results. The slice_thickness
parameter determines the thickness of the slice at 1.3 m and is default 0.06 m.
For tree point clouds with a lower point density at the stem (e.g. UAV-LS)
increasing the slice_thickness to for example 20 cm is crucial for DBH
measurements.
For buttressed trees dbh_pc
would result in:
In this case determining the DAB with dab_pc
is the better option. Also for
trees with DBH larger than 2 m and no branches at or below breast height this
function is recommended (even if buttresses are not present). With dab_pc
, the
diameter (from the optimal circle fitted through a 6mm thick horizontal slice)
is measured above the buttresses (if there are no buttresses the diameter is
measured at breast height). The height at which the horizontal slice is taken
(the height above buttresses), is determined iteratively, starting at breast
height. The average residual between the points and the fitted circle is
calculated. When the average residual exceeds a value of thresholdbuttress
times the radius, indicating a non-circular (irregular) stem shape and
presumably buttresses, and the maxbuttressheight is not exceeded, the process
is repeated with a new slice 6 mm higher than the previous one. When the
maxbuttressheight is exceeded the iterative process is restarted with a
thresholdbuttress increased with 0.0005. Also in this case a functional
DAB (fDAB) is reported.
# Measure DAB from the tree point cloud with default settings and plot the circle fitting DAB_out <- dab_pc(pc = tree_pc, plot = TRUE) DAB <- DAB_out$dab
Optimise the values of thresholdbuttress and or maxbuttressheight for your tree if default values do not lead to the desired results:
The dbh_pc
and dab_pc
do not yet work well on very slanted trees.
Therefore it is advised to always check the plots of the circle fitting.
For tree point clouds of which the stem of the tree is not sufficiently sampled
dbh_pc
and dab_pc
will not work due to a lack of stem points.
As a basis for crown metrics (e.g. projected crown area and 3D alpha crown
volume) the classify_crown_pc
is used to return the points from the tree
point cloud that belong to the crown. The crown is defined here as all points of
the tree except for the stem points below the first branch. The height where the
first branch emerges is iteratively determined (starting from minheight) as
the height where the diameter of the stem exceeds thresholdbranch multiplied
with the DBH or DAB.
# Classify the tree point cloud with default settings and plot the classification results C_out <- classify_crown_pc(pc = tree_pc, plot = TRUE) crown_pc <- C_out$crownpoints plot <- C_out$plot
Optimise the values of thresholdbranch and or minheight for your tree if default values do not lead to the desired results:
For non-buttressed trees, specify the previosuly determined thresholdR2 and slice_thickness parameters. For buttressed trees, the attribute buttress has to be set TRUE and previously chosen attributes thresholdbuttress and maxbuttressheight can be specified. It is recommended to increase the minheight for buttressed trees.
# Classify the tree point cloud of a buttressed tree and plot the classification results C_out <- classify_crown_pc( pc = tree_pc, minheight = 4, buttress = TRUE, plot = TRUE ) crown_pc <- C_out$crownpoints plot <- C_out$plot
The projected (crown) area of a tree point cloud can be measured with
projected_area_pc
which calculates the area of a concave hull (based on concaveman)
fitted to the input point cloud. The concavity (default=2) can be chosen and a
plot is made when parameter plot equals TRUE. If the input point cloud is the
full tree point cloud the output is the projected tree area. If the input is the
crown point cloud (determined with classify_crown_pc
or in your own way) the
output is the projected crown area.
# Measure the projected crown area and plot the results out <- projected_area_pc(pc = crown_pc, plot = TRUE) pca <- out$pa plot <- out$plot
The 3D alpha (crown) volume of tree point cloud can be measured with
alpha_volume_pc
which calculates the volume of the 3D alpha-shape (based on alphashape3d)
fitted to the (crown) points. The alpha (default=1) can be chosen and a 3D
plot is made when parameter plot equals TRUE.
# Measure the crown volume and generate 3D plot out <- alpha_volume_pc(pc = crown_pc, plot = TRUE) volume <- out$av
Often you want to check or optimise the performance of the (default) attributes
used in tree height, DBH, DAB, crown classification, projected area, and 3D
alpha volume calculation, for multiple tree point clouds in one folder. For this
purpose you can use plot_tree_height_pcs
(based on tree_height_pc
),
plot_circle_fit_pcs
(based on diameter_slice_pc
), plot_dbh_fit_pcs
(based on dbh_pc
), plot_dab_fit_pcs
(based on dab_pc
),
plot_crown_classification_pcs
(based on classify_crown_pc
), plot_pa_pcs
(based on projected_area_pc
), plot_av_pcs
(based on alpha_volume_pc
)
which return the tree height, diameter, DBH, DAB, PCA and CV for each tree in a
folder (PCs_path) and save the respective figures in the given output path
(OUT_path). First run these functions with default parameters and check
the rendered figures. Optionally change the attributes of the functions:
plot_tree_height_pcs
: r (when DTM is provided)plot_dbh_fit_pcs
: thresholdR2, slice_thicknessplot_dab_fit_pcs
: thresholdbuttress, maxbuttressheightclassify_crown_pc
: thresholdbranch, minheight plot_pca_pcs
& volume_crown_pc
: concavity & alpha# Tree height: # Plot the tree point clouds (check for outliers) # Specify dtm and r in case lower part of tree is not sampled Hs <- plot_tree_height_pcs( PCs_path = "path/to/point/clouds/folder/", extension = ".ply", OUT_path = "path/to/output/folder/", dtm = DTM, r = 2 ) # DBH: # try out different thresholdR2 and slice_thickness values when default values fail DBHs <- plot_dbh_fit_pcs( PCs_path = "path/to/point/clouds/folder/", extension = ".ply", OUT_path = "path/to/output/folder/", thresholdR2 = 0.0025, slice_thickness = 0.2 ) # DAB: # try out different values for thresholdbuttress and maxbuttressheight when default values fail DABs <- plot_dab_fit_pcs( PCs_path = "path/to/point/clouds/folder/", extension = ".las", OUT_path = "path/to/output/folder/", thresholdbuttress = 0.002, maxbuttressheight = 9 ) # Crown classification: # For non-buttressed trees: (buttress = FALSE,) specify thresholdR2 and slice_thickness chosen in previous step # For buttressed trees: buttress = TRUE, specify thresholdbuttress and maxbuttressheight chosen in previous step # Try out different values for thresholdbranch and minheight crown_pcs <- plot_crown_classification_pcs( PCs_path = "path/to/point/clouds/folder/", extension = ".txt", OUT_path = "path/to/output/folder/", thresholdbranch = 2, minheight = 3, buttress = TRUE, thresholdbuttress = 0.002, maxbuttressheight = 9 ) # Projected area # Try out a different value for concavity # indicate crown = TRUE if you want to calculate projected area on the crown points only # In this case: also specify the values for the crown classification chosen in previous step PAs <- plot_pa_pcs( PCs_path = "path/to/point/clouds/folder/", extension = ".ply", OUT_path = "path/to/output/folder/", concavity = 3, thresholdbranch = 2, minheight = 3, buttress = TRUE, thresholdbuttress = 0.002, maxbuttressheight = 9 ) # 3D alpha volume # Try out a different value for alpha # indicate crown = TRUE if you want to calculate projected area on the crown points only # In this case: also specify the values for the crown classification chosen in previous step AVs <- plot_av_pcs( PCs_path = "path/to/point/clouds/folder/", extension = ".las", OUT_path = "path/to/output/folder/", alpha = 2, thresholdbranch = 2, minheight = 3, buttress = TRUE, thresholdbuttress = 0.002, maxbuttressheight = 9 )
Once you have decided on default/optimised attributes, you can summarise all
point cloud structural metrics in one data.frame (and optionally export it to a
csv file) for all tree point clouds in a folder with
summary_basic_pointcloud_metrics
. If the plot parameter is TRUE, a summary
figure is rendered for each tree in the folder.
If you find different optimal attribute values for different trees, split the trees up in groups with the same parameter values (e.g. buttressed and non-buttressed, small and big trees, species1 and species2). Put the groups in different folders and run the summary function on the different folders separately. Afterwards the outputs can be combined.
# Summary with default setting for non-buttressed trees and plot the summary summary <- summary_basic_pointcloud_metrics(PCs_path = "path/to/point/clouds/folder_non-buttressed_trees/") # Summary with default setting (except minheight and buttress) for buttressed trees summary <- summary_basic_pointcloud_metrics( PCs_path = "path/to/point/clouds/folder_buttressed_trees/", minheight = 4, buttress = TRUE )
At the moment the ITSMe package contains TreeQSM based structural metrics defined by Åkerblom et al. (2017) and Terryn et al. (2020).
These are the metrics defined in Terryn et al. (2020) which were copied and adapted from Åkerblom et al. (2017) except for the branch angle ratio and the relative volume ratio which were new metrics. Definitions of the metrics can be found in the help files of the functions and the papers of Terryn et al. (2020) and Åkerblom et al. (2017). Normalisation according to Terryn et al. (2020) as well as Åkerblom et al. (2017) is possible through the normalisation parameter included in the functions of the metrics that were adapted by Terryn et al. (2020). If the tree point cloud is provided along with the TreeQSM in the functions, DBH or DAB and tree height values are based on the point clouds rather than the QSMs. When the buttress parameter is indicated "TRUE" the DAB instead of the DBH is used.
| structural metric | function name | input | | ------------------------------------------|:---------------------------------:|-----------------------:| | stem branch angle (degrees) | stem_branch_angle_qsm | TreeQSM | | stem branch cluster size | stem_branch_cluster_size_qsm | TreeQSM | | stem branch radius (-/m) | stem_branch_radius_qsm | TreeQSM (+point cloud) | | stem branch length (-/m) | stem_branch_length_qsm | TreeQSM (+point cloud) | | stem branch distance (-/m) | stem_branch_distance_qsm | TreeQSM (+point cloud) | | dbh tree height ratio | dbh_height_ratio_qsm | TreeQSM (+point cloud) | | dbh tree volume ratio (m$^{-2}$) | dbh_volume_ratio_qsm | TreeQSM (+point cloud) | | volume below 55 | volume_below_55_qsm | TreeQSM | | cylinder length volume ratio (m$^{-2}$) | cylinder_length_volume_ratio_qsm | TreeQSM | | shedding ratio | shedding_ratio_qsm | TreeQSM | | branch angle ratio | branch_angle_ratio_qsm | TreeQSM | | relative volume ratio | relative_volume_ratio_qsm | TreeQSM | | crown start height | crown_start_height_qsm | TreeQSM (+point cloud) | | crown height | crown_height_qsm | TreeQSM (+point cloud) | | crown evenness | crown_evenness_qsm | TreeQSM | | crown diameter crown height ratio | crown_diameterheight_ratio_qsm | TreeQSM (+point cloud) | | dbh minimum tree radius ratio | dbh_minradius_ratio_qsm | TreeQSM (+point cloud) |
All of the TreeQSM based functions (mentioned above) need one or multiple
components (e.g. cylinder, branch, treedata) of a TreeQSM in the form of lists
as their fist arguments. The ITSMe package provides a function read_tree_qsm
which reads a TreeQSM matlab file (.mat) and returns its' components in a list
(and optionally saves the TreeQSM components into the global environment when
global = TRUE). It requires the path to the TreeQSM .mat file as a first
argument and the TreeQSM version as a second (default = "2.4.0") argument.
# Read the TreeQSM file from its' specified path qsm <- read_tree_qsm(path = "path/to/treeqsm.mat") # Read the TreeQSM file of version "2.3.0" from its' specified path into the global environment qsm <- read_tree_qsm( path = "path/to/treeqsm.mat", version = "2.3.0", global = TRUE )
After reading in the TreeQSM, the 17 different structural metrics listed in the table above can easily be calculated. Some of these structural metrics rely on only one of the TreeQSM components.
# Calculate the stem branch angle and branch angle ratio from the branch component sba <- stem_branch_angle_qsm(branch = qsm$branch) bar <- branch_angle_ratio_qsm(branch = qsm$branch) # Calculate stem branch cluster size and crown evenness from the cylinder component sbcs <- stem_branch_cluster_size_qsm(cylinder = qsm$cylinder) ce <- crown_evenness_qsm(cylinder = qsm$cylinder) # Calculate the cylinder length volume ratio from the treedata component clvr <- cylinder_length_volume_ratio_qsm(treedata = qsm$treedata)
Other metrics rely on two of the TreeQSM components.
# Calculate the volume below 55 and the relative volume ratio from # the cylinder and treedata component vol_55 <- volume_below_55_qsm(cylinder = qsm$cylinder, treedata = qsm$treedata) relvol_ratio <- relative_volume_ratio_qsm( cylinder = qsm$cylinder, treedata = qsm$treedata ) # Calculate the shedding ratio from the branch and treedata component shed_ratio <- shedding_ratio_qsm(branch = qsm$branch, treedata = qsm$treedata)
For some of the metrics which involve the DBH/DAB and/or the tree height, the tree point cloud of the tree can be provided as an input besides the TreeQSM data. This is because (in general) the DBH/DAB and tree height are more accurately measured from the complete tree point cloud. When the DBH is involved the DAB will be used if buttress = TRUE. Also specify the attribute values determined for the DBH or DAB measurement from a tree point cloud (see previous sections),
# Read the tree point cloud tree_pc <- read_tree_pc(path = "path/to/point/cloud.txt") # Calculate the dbh min tree radius and volume ratio using additional point cloud data dmrr <- dbh_minradius_ratio_qsm( treedata = qsm$treedata, cylinder = qsm$cylinder, pc = tree_pc, thresholdR2 = 0.0025 ) dvr <- dbh_volume_ratio_qsm( treedata = qsm$treedata, pc = tree_pc, slice_thickness = 0.1 ) # Calculate the dbh height ratio of a buttressed tree using additional point cloud data # Specify the optimised thresholdbuttress and maxbuttressheight when needed dhr <- dbh_height_ratio_qsm( treedata = qsm$treedata, pc = tree_pc, buttress = TRUE, thresholdbuttress = 0.002, maxbuttressheight = 5 ) # Calculate the crown start height, crown height and crown diameter height ratio # using additional point cloud data csh <- crown_start_height_qsm( treedata = qsm$treedata, cylinder = qsm$cylinder, pc = tree_pc ) ch <- crown_height_qsm( treedata = qsm$treedata, cylinder = qsm$cylinder, pc = tree_pc ) cdhr <- crown_diameterheight_ratio_qsm( treedata = qsm$treedata, cylinder = qsm$cylinder, pc = tree_pc )
Normalisation according to Terryn et al. (2020) as well as Åkerblom et al. (2017) is possible through the normalisation parameter included in the functions of the metrics that were adapted by Terryn et al. (2020).
# Calculate the stem branch radius according to Åkerblom et al. (2017) sbr <- stem_branch_radius_qsm( cylinder = qsm$cylinder, treedata = qsm$treedata, normalisation = "parentcylinder" ) # Calculate the stem branch length according to Terryn et al. (2020) sbl <- stem_branch_length_qsm( branch = qsm$branch, treedata = qsm$treedata, normalisation = "treeheight" ) # Calculate the stem branch distance according to Åkerblom et al. (2017) and # using point cloud information sbd <- stem_branch_distance_qsm( cylinder = qsm$cylinder, treedata = qsm$treedata, normalisation = "dbh", pc = tree_pc, buttress = TRUE, thresholdbuttress = 0.002, maxbuttressheight = 5 )
you can summarise all structural metrics defined by Terryn et al. (2020) in one
data.frame (and optionally export it to a csv file) for all TreeQSMs in a folder
with summary_qsm_metrics
. Choose the normalisation for
stem_branch_radius_qsm
, stem_branch_length_qsm
, and
stem_branch_distance_qsm
. In case you want to use tree point cloud
information, specify the folder (PCs_path) and extension of the tree point
cloud files, indicate if the trees have buttresses (buttress) and specify if
you want to use non-default argument values (thresholdR2, slice_thickness,
thresholdbuttress and maxbuttressheight) to calculate the DAB (see chapter
on point cloud metrics). If you want the data.frame to be exported to a csv
file, specify the path to the output folder (OUT_path).
The QSM files have to be of the format xxx_qsm.mat (xxx is tree id and can consist of multiple parts e.g. plot name, tree number) or xxx_qsm_0.mat (0 at the end is for example the n-th QSM that is made for tree 000). The qsm file can also contain multiple QSMs, in this case set parameter multiple = TRUE. When multiple QSMs are provided for one tree the mean and the standard deviation of the values of the different QSMs of one tree are also returned in additional data.frames. When provided, the tree point clouds files have to be of the format xxx_pc in order to link the tree point cloud to its' respective treeQSM.
# Run the summary function with default settings (without point cloud info) summary_qsm_metrics(QSMs_path = "path/to/treeqsm/folder/") # Run the summary function with multiple QSMs per *mat file summary_qsm_metrics(QSMs_path = "path/to/treeqsm/folder/", multiple = TRUE) # Run the summary function with default settings with point cloud info summary_qsm_metrics( QSMs_path = "path/to/treeqsms/folder/", PCs_path = "path/to/point/clouds/folder/" ) # Run the summary function with non-default settings with point cloud info summary_qsm_metrics( QSMs_path = "path/to/treeqsms/folder/", version = "2.4.0", sbr_normalisation = "parentcylinder", sbl_normalisation = "dbh", sbd_normalisation = "dbh", PCs_path = "path/to/point/clouds/folder/", extension = ".ply", buttress = TRUE, thresholdbuttress = 0.002, maxbuttressheight = 5, OUT_path = "path/to/output/folder/" )
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.