Algorithms described in "Developing non-destructive species-specific tree allometry with terrestrial laser scanning" (Stovall et al. 2022) for estimating tree-level component biomass from terrestrial laser scanning (TLS) data.
First we must separate isolated tree TLS data into trunk and branch point clouds. We reccomend CloudCompare to open point clouds and isolate the trunk and branch tree components.
Start with a view above or below the tree and use the scissor tool to clip the interior trunk points. Continue to clip as much of the branch points from the trunk point cloud as possible. When complete, save the branch and trunk point clouds as separate files (.csv, .asc, or similar). These files will be used with the OHM algorithm to estimate tree-level biomass.
In a recent study, we followed this approach, separating trunk and tree crown, allowing us to estimate biomass with the OHM algorithm. Here you can see three example trees of different study species:
We will be using the Lodgepole Pine tree pictured on the right in this example.
require(circular)
require(lidR)
require(viridis)
require(rgl)
library(fpc)
source("R/2021_OHM_FUN.R")
#Where is the TLS data?
files.trunk<-list.files("data", pattern="trunk.csv", full.names = TRUE, recursive=TRUE)
files.branch<-list.files("data", pattern="branch.csv", full.names = TRUE, recursive=TRUE)
tree.ID<-list.dirs("data", full.names = FALSE)
#Making a dataframe of files for the tree components
files<-data.frame(treeID=tree.ID[-1],
trunk=files.trunk,
branch=files.branch)
#Read in TLS data
ohm.files<-read.csv.ohm.input(files[1,])
#We can split the files into trunk and biomass elements
trunk <- ohm.files[[1]]
branch <- ohm.files[[2]]
OHM.output<-OHM.tree(trunk = trunk,
branch = branch,
sg=0.4,
interval = 0.1, buff = 0.1, outlier_pct = 20,
vox.res=0.1, vol.correction=1)
We can look at the OHM algorithm output in several ways. First the trunk radius fits:
#Look at the trunk radius fits
plot(OHM.output$all.circle.fits$r, OHM.output$all.circle.fits$z, col="grey", main='Trunk Radius Fits',
ylab='Height (m)', xlab= "Radius (m)")
lines(OHM.output$filtered.circle.fits$r, OHM.output$filtered.circle.fits$z, col="forestgreen")
We can also look at the cumulative component biomass (brown = trunk; green = crown) of individual trees with height:
plot(OHM.output$trunk.volume$z, cumsum(OHM.output$trunk.volume$vol), col="white", xlab='Height (m)', ylab= "Volume (m^3)",
main="Cumulative Tree \n Component Volume")
lines(OHM.output$trunk.volume$z, cumsum(OHM.output$trunk.volume$vol), col="brown")
lines(OHM.output$branch.volume$z, cumsum(OHM.output$branch.volume$vol.log), col="forestgreen")
Finally, what is the component biomass of the modeled tree?
print(paste("Total tree biomass estimate is",
OHM.output$biomass[3]
))
print(paste("Trunk tree biomass estimate is",
OHM.output$biomass[1]
))
print(paste("Branch tree biomass estimate is",
OHM.output$biomass[2]
))
From this we can see that the total tree-level biomass estimate using OHM had ~18% error or overpredicted by around 52 kg. In our study we found the TLS-based biomass estimates had an RMSE of ~19% across all species, ranging from 13-24%, depending on species.
If you have destructive data and TLS data for coniferous trees give OHM a try and let us know your results!
Stovall, A.E.L., Vorster, A.G., Anderson, R.S., Evangelista, P.H., Shugart, H.H., 2017. Non-destructive aboveground biomass estimation of coniferous trees using terrestrial LiDAR. Remote Sensing of Environment 200, 31–42. https://doi.org/10.1016/j.rse.2017.08.013


