-
Notifications
You must be signed in to change notification settings - Fork 0
/
04_chm.qmd
176 lines (126 loc) · 7.6 KB
/
04_chm.qmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
---
title: "Canopy Height Models"
---
```{r, echo = FALSE, warnings = FALSE}
library(rgl)
r3dDefaults <- rgl::r3dDefaults
m <- structure(c(0.921, -0.146, 0.362, 0, 0.386, 0.482, -0.787, 0,
-0.06, 0.864, 0.5, 0, 0, 0, 0, 1), .Dim = c(4L, 4L))
r3dDefaults$FOV <- 50
r3dDefaults$userMatrix <- m
r3dDefaults$zoom <- 0.75
knitr::opts_chunk$set(
comment = "#>",
collapse = TRUE,
fig.align = "center")
rgl::setupKnitr(autoprint = TRUE)
options(lidR.progress = FALSE)
```
## Relevant Resources
[Code](https://github.com/tgoodbody/lidRtutorial/blob/main/code/tutorials/04_chm.R)
[lidRbook section](https://r-lidar.github.io/lidRbook/chm.html)
## Overview
This code demonstrates the creation of a Canopy Height Model (CHM) using LiDAR data. It shows different algorithms for generating CHMs and provides options for adjusting resolution, sub-circle size, and filling empty pixels.
## Environment
```{r clear_warnings, warnings = FALSE, message = FALSE}
# Clear environment
rm(list = ls(globalenv()))
# Load packages
library(lidR)
library(microbenchmark)
library(terra)
```
## Data Preprocessing
In this section, we load the LiDAR data, set a random fraction filter to reduce point density, and visualize the resulting LiDAR point cloud.
```{r load_lidar_data}
# Load LiDAR data and reduce point density
las <- readLAS(files = "data/MixedEucaNat_normalized.laz", filter = "-keep_random_fraction 0.4 -set_withheld_flag 0")
# Visualize the LiDAR point cloud
plot(las)
```
## Point-to-Raster Based Algorithm
In this section, we demonstrate a simple method for generating Canopy Height Models (CHMs) that assigns the elevation of the highest point to each pixel.
```{r point_to_raster_algorithm}
# Generate the CHM using a simple point-to-raster based algorithm
chm <- rasterize_canopy(las = las, res = 2, algorithm = p2r())
# Visualize the CHM
plot(chm, col = height.colors(50))
```
In the first code chunk, we generate a Canopy Height Model (CHM) using a point-to-raster based algorithm. The `rasterize_canopy` function with the `p2r()` algorithm assigns the elevation of the highest point within each grid cell to the corresponding pixel. The resulting CHM is then visualized using the `plot()` function.
```{r point_to_raster_algorithm_equivalent}
# The above method is strictly equivalent to using pixel_metrics to compute max height
chm <- pixel_metrics(las = las, func = ~max(Z), res = 2)
# Visualize the CHM
plot(chm, col = height.colors(50))
```
The code chunk above shows that the point-to-raster based algorithm is equivalent to using `pixel_metrics` with a function that computes the maximum height (`max(Z)`) within each grid cell. The resulting CHM is visualized using the `plot()` function.
```{r point_to_raster_algorithm_optimized}
# However, the rasterize_canopy algorithm is optimized
microbenchmark::microbenchmark(canopy = rasterize_canopy(las = las, res = 1, algorithm = p2r()),
metrics = pixel_metrics(las = las, func = ~max(Z), res = 1),
times = 10)
```
The above code chunk uses `microbenchmark::microbenchmark()` to compare the performance of the `rasterize_canopy()` function with `p2r()` algorithm and `pixel_metrics()` function with `max(Z)` for maximum height computation. It demonstrates that the `rasterize_canopy()` function is optimized for generating CHMs.
```{r point_to_raster_algorithm_higher_resolution}
# Increasing the resolution results in fewer empty pixels
chm <- rasterize_canopy(las = las, res = 1, algorithm = p2r())
plot(chm, col = height.colors(50))
```
By increasing the resolution of the CHM (reducing the grid cell size), we get a more detailed representation of the canopy with fewer empty pixels.
```{r point_to_raster_algorithm_subcircle}
# Using the 'subcircle' option turns each point into a disc of 8 points with a radius r
chm <- rasterize_canopy(las = las, res = 0.5, algorithm = p2r(subcircle = 0.15))
plot(chm, col = height.colors(50))
```
The `rasterize_canopy()` function with the `p2r()` algorithm allows the use of the `subcircle` option, which turns each LiDAR point into a disc of 8 points with a specified radius. This can help to capture more fine-grained canopy details in the resulting CHM.
```{r point_to_raster_algorithm_larger_subcircle_radius}
# Increasing the subcircle radius, but it may not have meaningful results
chm <- rasterize_canopy(las = las, res = 0.5, algorithm = p2r(subcircle = 0.8))
plot(chm, col = height.colors(50))
```
Increasing the `subcircle` radius may not necessarily result in meaningful CHMs, as it could lead to over-smoothing or loss of important canopy information.
```{r point_to_raster_algorithm_fill_empty_pixels}
# We can fill empty pixels using TIN interpolation
chm <- rasterize_canopy(las = las, res = 0.5, algorithm = p2r(subcircle = 0.15, na.fill = tin()))
plot(chm, col = height.colors(50))
```
The `p2r()` algorithm also allows filling empty pixels using TIN (Triangulated Irregular Network) interpolation, which can help in areas with sparse LiDAR points to obtain a smoother CHM.
## Triangulation Based Algorithm
In this section, we demonstrate a triangulation-based algorithm for generating CHMs.
```{r triangulation_algorithm}
# Triangulation of first returns to generate the CHM
chm <- rasterize_canopy(las = las, res = 1, algorithm = dsmtin())
plot(chm, col = height.colors(50))
```
The `rasterize_canopy()` function with the `dsmtin()` algorithm generates a CHM by performing triangulation on the first returns from the LiDAR data. The resulting CHM represents the surface of the canopy.
```{r triangulation_algorithm_higher_resolution}
# Increasing the resolution results in a more detailed CHM
chm <- rasterize_canopy(las = las, res = 0.5, algorithm = dsmtin())
plot(chm, col = height.colors(50))
```
Increasing the resolution of the CHM using the `res` argument provides a more detailed representation of the canopy, capturing finer variations in the vegetation.
```{r triangulation_algorithm_pitfree}
# Using the Khosravipour et al. pit-free algorithm with specified thresholds and maximum edge length
thresholds <- c(0, 5, 10, 20, 25, 30)
max_edge <- c(0, 1.35)
chm <- rasterize_canopy(las = las, res = 0.5, algorithm = pitfree(thresholds, max_edge))
plot(chm, col = height.colors(50))
```
The `rasterize_canopy` function can also use the Khosravipour et al. pit-free algorithm with specified height thresholds and a maximum edge length to generate a CHM. This algorithm aims to correct depressions in the CHM surface.
```{r triangulation_algorithm_subcircle}
# Using the 'subcircle' option with the pit-free algorithm
chm <- rasterize_canopy(las = las, res = 0.5, algorithm = pitfree(thresholds, max_edge, 0.1))
plot(chm, col = height.colors(50))
```
The `subcircle` option can be used with the pit-free algorithm to create finer CHMs with subcircles for each LiDAR point, similar to the point-to-raster based algorithm.
## Post-Processing
Usually, CHMs can be post-processed by smoothing or other manipulations. Here, we demonstrate post-processing using the `terra::focal()` function for smoothing.
```{r post_processing}
# Post-process the CHM using the 'terra' package and focal() function for smoothing
ker <- matrix(1, 3, 3)
schm <- terra::focal(chm, w = ker, fun = mean, na.rm = TRUE)
# Visualize the smoothed CHM
plot(schm, col = height.colors(50))
```
## Conclusion
This tutorial covered different algorithms for generating Canopy Height Models (CHMs) from LiDAR data using the `lidR` package in R. It includes point-to-raster-based algorithms and triangulation-based algorithms, as well as post-processing using the `terra` package. The code chunks are well-labeled to help the audience navigate through the tutorial easily.