Commit 2c27c586 authored by Monnet Jean-Matthieu's avatar Monnet Jean-Matthieu
Browse files

Modified data folder and updated tree.detection

parent 30b58523
% This file was created with JabRef 2.10b2.
% Encoding: UTF-8
@PhdThesis{Monnet11c,
Title = {Using airborne laser scanning for mountain forests mapping: support vector regression for stand parameters estimation and unsupervised training for treetop detection.},
Author = {Monnet, Jean-Matthieu},
School = {Université de Grenoble},
Year = {2011},
Abstract = {Numerous studies have shown the potential of airborne laser scanning for the mapping of forest resources. However, the application of this remote sensing technique to complex forests encountered in mountainous areas requires further investigation. In this thesis, the two main methods used to derive forest information are tested with airborne laser scanning data acquired in the French Alps, and adapted to the constraints of mountainous environments. In particular, a framework for unsupervised training of treetop detection is proposed, and the performance of support vector regression combined with dimension reduction for forest stand parameters estimation is evaluated.},
Keywords = {Remote sensing, airborne laser scanning, LiDAR, forest mapping, support vector regression, unsupervised training, treetop detection},
Owner = {jimbo},
Timestamp = {2014.05.15},
Url = {http://tel.archives-ouvertes.fr/tel-00652698/fr/}
}
@Article{Monnet2014,
Title = {Cross-Correlation of Diameter Measures for the Co-Registration of Forest Inventory Plots with Airborne Laser Scanning Data},
Author = {Monnet, Jean-Matthieu and Mermin, Éric},
Journal = {Forests},
Year = {2014},
Number = {9},
Pages = {2307--2326},
Volume = {5},
Doi = {10.3390/f5092307},
ISSN = {1999-4907},
Owner = {jean-matthieu},
Timestamp = {2018.05.04},
Url = {http://www.mdpi.com/1999-4907/5/9/2307}
}
@inproceedings{Monnet10,
TITLE = {{Tree top detection using local maxima filtering: a parameter sensitivity analysis}},
AUTHOR = {Monnet, Jean-Matthieu and Mermin, Eric and Chanussot, Jocelyn and Berger, Fr{\'e}d{\'e}ric},
URL = {https://hal.archives-ouvertes.fr/hal-00523245},
BOOKTITLE = {{10th International Conference on LiDAR Applications for Assessing Forest Ecosystems (Silvilaser 2010)}},
ADDRESS = {Freiburg, Germany},
HAL_LOCAL_REFERENCE = {D{\'e}partement Images et Signal},
PAGES = {9 p.},
YEAR = {2010},
MONTH = Sep,
KEYWORDS = {DETECTION ; ANALYSE DE SENSIBILITE ; LASER ; FORET ; CANOPEE ; LIDAR DETECTION ; ALGORITHME DE DETECTION ; TREE DETECTION},
PDF = {https://hal.archives-ouvertes.fr/hal-00523245/file/GR2010-PUB00029356.pdf},
HAL_ID = {hal-00523245},
HAL_VERSION = {v1},
}
@article{Eysn15,
title={A Benchmark of Lidar-Based Single Tree Detection Methods Using Heterogeneous Forest Data from the Alpine Space},
volume={6},
ISSN={1999-4907},
url={http://dx.doi.org/10.3390/f6051721},
DOI={10.3390/f6051721},
number={12},
journal={Forests},
publisher={MDPI AG},
author={Eysn, Lothar and Hollaus, Markus and Lindberg, Eva and Berger, Frédéric and Monnet, Jean-Matthieu and Dalponte, Michele and Kobal, Milan and Pellegrini, Marco and Lingua, Emanuele and Mongus, Domen and et al.},
year={2015},
month={May},
pages={1721–1747}
}
This diff is collapsed.
--- ---
title: "Workflow for tree detection from ALS data" title: "R workflow for tree segmentation from ALS data"
author: "Jean-Matthieu Monnet" author: "Jean-Matthieu Monnet"
date: "`r Sys.Date()`" date: "`r Sys.Date()`"
output: output:
pdf_document: default pdf_document: default
html_document: default html_document: default
bibliography: workflow.treedetection.bib bibliography: "./bib/bibliography.bib"
--- ---
```{r setup, include=FALSE} ```{r setup, include=FALSE}
...@@ -19,24 +19,17 @@ knitr::opts_chunk$set(fig.align = "center") ...@@ -19,24 +19,17 @@ knitr::opts_chunk$set(fig.align = "center")
library(lidR) library(lidR)
``` ```
--- ---
The code below presents a tree detection workfow from Airborne Laser Scanning (ALS) data. Workflow is based on functions from R packages `lidaRtRee` and `lidR`. The code below presents a tree segmentation workflow from Airborne Laser Scanning (lidar remote sensing) data. The workflow is based on functions from R packages `lidaRtRee` and `lidR`, and it includes the following steps:
* treetop detection and crown segmentation * treetop detection,
+ accuracy assessment with field inventory + crown segmentation,
+ accuracy assessment with field inventory,
+ species classification + species classification
Licence: CC-BY Steps 1 and 3 are documented in [@Monnet10; @Monnet11c]. The detection performance of this algorithm was evaluated in a benchmark [@Eysn15].
Source page: https://gitlab.irstea.fr/jean-matthieu.monnet/lidaRtRee/wikis/tree-detection-workflow
Changelog:
* Jan 26, 2021: checked compatibility with package `lidR` 3.1.0 and `lidaRtRee` 3.0.0 Licence: CC-BY / [source page](https://gitlab.irstea.fr/jean-matthieu.monnet/lidartree_tutorials/-/blob/master/tree.detection.Rmd)
+ Oct 12, 2020: checked compatibility with package `lidR` 2.2
+ Jan 17, 2020: checked compatibility with package `lidR` 2.2
+ Dec 6, 2019: `cloudMetrics` function used for point cloud stats in segments
+ Feb 4, 2019: updated for compatibilty with packages `lidR` 2.0.0 and `lidaRtRee` 2.0.0
+ May 18, 2018: first release
## Material ## Material
### Field inventory ### Field inventory
...@@ -50,7 +43,7 @@ data(treeinventorychablais3, package = "lidaRtRee") ...@@ -50,7 +43,7 @@ data(treeinventorychablais3, package = "lidaRtRee")
Otherwise you can load your own data provided positions and heights are measured. Otherwise you can load your own data provided positions and heights are measured.
```{r prepareTreeInventory, eval=FALSE} ```{r prepareTreeInventory, eval=FALSE}
# import field inventory # import field inventory
fichier <- "lidaRtRee/inst/extdata/chablais3_listeR.csv" fichier <- "chablais3_listeR.csv"
tree.inventory <- read.csv(file = fichier, sep = ";", header = F) tree.inventory <- read.csv(file = fichier, sep = ";", header = F)
names(tree.inventory) <- c("x", "y", "d", "h", "n", "s", "e", "t") names(tree.inventory) <- c("x", "y", "d", "h", "n", "s", "e", "t")
# save as rda for later access # save as rda for later access
...@@ -141,13 +134,13 @@ chm <- dsm - dtm ...@@ -141,13 +134,13 @@ chm <- dsm - dtm
``` ```
```{r plotDEMs, echo=FALSE, fig.width = 12, fig.height = 4.5, out.width='100%'} ```{r plotDEMs, echo=FALSE, fig.width = 12, fig.height = 4.5, out.width='100%'}
par(mfrow=c(1,3)) par(mfrow = c(1, 3))
# display DTM # display DTM
raster::plot(dtm,asp=1, main="DTM") raster::plot(dtm, main = "DTM")
# display DSM # display DSM
raster::plot(dsm,asp=1, main="DSM") raster::plot(dsm, main = "DSM")
# display CHM # display CHM
raster::plot(chm,asp=1, main="CHM") raster::plot(chm, main = "CHM")
``` ```
### Visual comparison of field inventory and ALS data ### Visual comparison of field inventory and ALS data
...@@ -172,7 +165,7 @@ Displaying inventoried trees on the CHM shows a pretty good correspondance of cr ...@@ -172,7 +165,7 @@ Displaying inventoried trees on the CHM shows a pretty good correspondance of cr
```{r plotPlot, include = TRUE, out.width = '60%', fig.dim=c(6.5, 4.5), warnings=FALSE} ```{r plotPlot, include = TRUE, out.width = '60%', fig.dim=c(6.5, 4.5), warnings=FALSE}
# display CHM # display CHM
raster::plot(chm,asp=1, col=gray(seq(0,1,1/255)), raster::plot(chm, col=gray(seq(0,1,1/255)),
main ="Canopy Height Model and tree positions") main ="Canopy Height Model and tree positions")
# add inventoried trees # add inventoried trees
lidaRtRee::plotTreeInventory(treeinventorychablais3[,c("x","y")], lidaRtRee::plotTreeInventory(treeinventorychablais3[,c("x","y")],
...@@ -197,13 +190,13 @@ segms <- lidaRtRee::treeSegmentation(chm) ...@@ -197,13 +190,13 @@ segms <- lidaRtRee::treeSegmentation(chm)
# #
par(mfrow=c(1,3)) par(mfrow=c(1,3))
# display pre-processed chm # display pre-processed chm
raster::plot(segms$smoothed.dem,asp=1, main="Pre-processed CHM") raster::plot(segms$smoothed.dem, main="Pre-processed CHM")
# display selected local maxima # display selected local maxima
raster::plot(segms$local.maxima,asp=1, main="Selected local maxima") raster::plot(segms$local.maxima, main="Selected local maxima")
# display segments, except ground segment # display segments, except ground segment
dummy <- segms$segments.id dummy <- segms$segments.id
dummy[dummy==0] <- NA dummy[dummy==0] <- NA
raster::plot(dummy %% 8,asp=1, main="Segments (random colors)", col=rainbow(8), legend=FALSE) raster::plot(dummy %% 8, main="Segments (random colors)", col=rainbow(8), legend=FALSE)
``` ```
### Tree extraction ### Tree extraction
...@@ -230,9 +223,8 @@ head(trees, n=3L) ...@@ -230,9 +223,8 @@ head(trees, n=3L)
v.segments <- raster::rasterToPolygons(segms[[2]], dissolve=T) v.segments <- raster::rasterToPolygons(segms[[2]], dissolve=T)
# #
# display initial image # display initial image
raster::plot(chm,asp=1, col=gray(seq(0,1,1/255)), raster::plot(chm, col=gray(seq(0,1,1/255)),
main ="Canopy Height Model main ="CHM and detected positions")
and detected positions")
# display segments border # display segments border
sp::plot(v.segments,border="white",add=T) sp::plot(v.segments,border="white",add=T)
# display plot mask # display plot mask
...@@ -244,9 +236,9 @@ graphics::points(trees$x, trees$y, col="blue", cex=trees$h/20) ...@@ -244,9 +236,9 @@ graphics::points(trees$x, trees$y, col="blue", cex=trees$h/20)
## Detection evaluation ## Detection evaluation
### Tree matching ### Tree matching
To assess detection accuracy, reference (field) trees should be linked to detected trees. Despite the possibility of error, automated matching has the advantage of making the comparison of results from different algorithms and settings reproducible and objective. The algorithm presented below is based on the 3D distance between detected treetops and inventory positions and heights [@Monnet2014]. To assess detection accuracy, reference (field) trees should be linked to detected trees. Despite the possibility of error, automated matching has the advantage of making the comparison of results from different algorithms and settings reproducible and objective. The algorithm presented below is based on the 3D distance between detected treetops and inventory positions and heights [@Monnet10].
```{r plotMached, include=TRUE, out.width = '60%', fig.dim=c(6.5, 4.5)} ```{r plotMached, include=TRUE, out.width = '70%', fig.dim=c(6.5, 4.5)}
# match detected treetops with field trees based on relative distance of apices # match detected treetops with field trees based on relative distance of apices
matched <- lidaRtRee::treeMatching(treeinventorychablais3[selec,c("x","y","h")], matched <- lidaRtRee::treeMatching(treeinventorychablais3[selec,c("x","y","h")],
cbind(trees@coords, trees$h)) cbind(trees@coords, trees$h))
...@@ -334,7 +326,7 @@ trees <- base::merge(trees, metrics, by.x="id", by.y="seg.id", all.x=T) ...@@ -334,7 +326,7 @@ trees <- base::merge(trees, metrics, by.x="id", by.y="seg.id", all.x=T)
Plotting the reference trees with the mean intensity of lidar points in the segments shows that when they are dominant, broadleaf trees tend to have higher mean intensity than coniferous trees. Plotting the reference trees with the mean intensity of lidar points in the segments shows that when they are dominant, broadleaf trees tend to have higher mean intensity than coniferous trees.
. .
```{r Intensity, include=TRUE, out.width = '60%', fig.dim=c(6.5, 4.5)} ```{r Intensity, include=TRUE, out.width = '70%', fig.dim=c(6.5, 4.5)}
# create raster of segment' mean intensity # create raster of segment' mean intensity
r.mean.intensity.segm <- segms[["segments.id"]] r.mean.intensity.segm <- segms[["segments.id"]]
# match segment id with id in metrics data.frame # match segment id with id in metrics data.frame
...@@ -524,7 +516,7 @@ Be aware that in case tree segments are vectorized, some obtained polygons might ...@@ -524,7 +516,7 @@ Be aware that in case tree segments are vectorized, some obtained polygons might
rm(list=ls()) rm(list=ls())
# BUILD CATALOG OF FILES # BUILD CATALOG OF FILES
# folder containing the files # folder containing the files
lazdir <- "./data.ABA.model/tiles.norm.laz/" lazdir <- "./data/forest.structure.metrics"
# build catalog # build catalog
cata <- lidR::catalog(lazdir) cata <- lidR::catalog(lazdir)
# set coordinate system # set coordinate system
...@@ -535,7 +527,7 @@ lidR::sensor(cata) <- "ALS" ...@@ -535,7 +527,7 @@ lidR::sensor(cata) <- "ALS"
# BATCH PROCESSING PARAMETERS # BATCH PROCESSING PARAMETERS
# tile size to split area into chunks to process # tile size to split area into chunks to process
# trade-off between RAM capacity VS total number of tiles to process # trade-off between RAM capacity VS total number of tiles to process
tile.size <- 200 # here 30 for example purpose with a small chm tile.size <- 70 # here 70 for example purpose with small area
# buffer size: around each tile to avoid border effects on segmentation results # buffer size: around each tile to avoid border effects on segmentation results
# trade-off between making sure a whole tree crown is processed in case its top is on the border VS duplicate processing # trade-off between making sure a whole tree crown is processed in case its top is on the border VS duplicate processing
buffer.size <- 10 # 5 m is minimum, 10 is probably better depending on tree size buffer.size <- 10 # 5 m is minimum, 10 is probably better depending on tree size
...@@ -678,14 +670,17 @@ if (vectorize.trees) ...@@ -678,14 +670,17 @@ if (vectorize.trees)
The following image displays the results for the whole area. The following image displays the results for the whole area.
```{r batch.plot, include=TRUE, eval = FALSE, out.width = '100%', fig.dim=c(8, 8)} ```{r batch.plot, include=TRUE, out.width = '90%', fig.dim=c(8.5, 5.5)}
# threshold outsiders in chm # threshold outsiders in chm
chm.all[chm.all > 40] <- 40 chm.all[chm.all > 40] <- 40
chm.all[chm.all < 0] <- 0 chm.all[chm.all < 0] <- 0
# plot chm # display chm
raster::plot(chm.all) raster::plot(chm.all,
main ="Canopy Height Model and segmented trees")
# display segments border
sp::plot(v.trees, border = "white", add = T)
# add trees # add trees
plot(trees, cex = trees$h/80, add = TRUE) plot(trees, cex = trees$h/40, add = TRUE)
``` ```
The following lines save outputs to files. The following lines save outputs to files.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment