Custom voxel metrics catalog processing #738
Replies: 5 comments 4 replies
-
Please provide a minimal reproducible example including data (data shipped with the package if possible) and removing as much code as possible to get a meaningful output but avoid sharing hundreds of lines of code. At first glance, I see one more or less obvious problem. Your voxel resolution if not a multiple of your pixel resolution. Thus, the voxel grid is not aligned with the pixel grid, and you missed some data by computing a sub grid inside a grid that does not contain the adequate data for the sub grid. But, as I said, the code is too complex to grasp the problem just reading the code. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the input! I think you are correct that alignment is at least one of the issues. However, even changing the voxel resolution to 2 produces some differences in chunks. These differences are even more apparent however when I play with the opt_chunk_alignment() option (see bottom image). Note where chunks overlap only a portion of the catalog, the voxel values are generally higher. Most of the catalogs I work with are not square if that means anything. I have run an example on the catalog provided in the lidR book. using the functions provided above. However, I changed the resolution to 2 in the ind_vox() and vox_sum_raster() functions. I also edited the original post so the functions can be copied and pasted to produce the results from the following code: ## Load in catalog example from https://r-lidar.github.io/lidRbook/engine.html
ctg <- readLAScatalog("ctg_example/")
opt_chunk_buffer(ctg) <- 10
opt_chunk_size(ctg) <- 250
opt_select(ctg) <- "xyzicr"
opt_wall_to_wall(ctg) <- TRUE
opt_chunk_alignment(ctg) <- c(0,0)
opt_progress(ctg) <- TRUE
opt_stop_early(ctg) <- TRUE
plot(ctg, chunk = TRUE)
layer_list <- catalog_apply(ctg, all_layers_catalog)
rasters <- lapply(layer_list, terra::unwrap)
rasters <- do.call(terra::merge, rasters)
plot(rasters[[2]]) Here is the output of the above code with some chunk borders circled in yellow (voxel res = 2; chunk alignment is c(0,0)): Here is the output of the above code with a different alignment. Some chunk borders are circled in yellow (voxel res = 2; chunk alignment is c(70,0)): |
Beta Was this translation helpful? Give feedback.
-
@RCBlackburn - are you saying that the above code you provided produces the issue (pictured) with the example .laz files from the book (which has all the .laz files to build the catalog)? Specifically, the nine files in section 14.3 read as a catalog produce what you circled? |
Beta Was this translation helpful? Give feedback.
-
@bi0m3trics That is correct. I wanted to provide a sample using the same chunk size that I found the issue with. @Jean-Romain here is some code with the Megaplot.laz in the library. It is harder to see with a smaller catalog with a pixel_metrics() resolution of 10m. However, if you set the pixel_metrics() resolution to 4m it is more visible. I've also included the GitHub repository install for the voxel functions. There are minor differences with the original voxel function posted in this thread and the lidRattR package, but not regarding this issue. devtools::install_github("RCBlackburn/lidRattR")
library(lidRattR)
library(lidR)
library(terra)
LASfile <- system.file("extdata", "Megaplot.laz", package="lidR")
ctg = readLAScatalog(LASfile)
opt_chunk_buffer(ctg) <- 10
opt_chunk_size(ctg) <- 100
opt_select(ctg) <- "xyzicr"
opt_wall_to_wall(ctg) <- TRUE
opt_chunk_alignment(ctg) <- c(0,0)
opt_progress(ctg) <- TRUE
opt_stop_early(ctg) <- TRUE
plot(ctg, chunk = TRUE)
all_layers_catalog = function(cluster)
{
las = readLAS(cluster)
las = las_update(las)
if (lidR::is.empty(las)) return(NULL)
las = filter_poi(las, Classification != LASNOISE)
if (lidR::is.empty(las)) return(NULL)
las = normalize_height(las, knnidw(k = 8, p = 2))
las = filter_poi(las, Z < 50 & Z >= 1.37 )
if (lidR::is.empty(las)) return(NULL)
las <- decimate_points(las, random_per_voxel(res = 1, n = 8))
if (lidR::is.empty(las)) return(NULL)
### voxel metrics
las_vox <- ind_vox(las, res = 2)
vmetrics <- pixel_metrics(las_vox,
func = vox_sum_raster(SVi, FRDi, PDi, PDi_above, vox_res = 2),
res = 10)
tile0_ext = lidR::ext(las)
vmetrics <- crop(vmetrics, tile0_ext)
return(wrap(vmetrics))
}
layer_list <- catalog_apply(ctg, all_layers_catalog)
raster <- lapply(layer_list, terra::unwrap)
raster <- do.call(terra::merge, raster)
plot(raster[[2]]) |
Beta Was this translation helpful? Give feedback.
-
So, my guess was correct. First let me rewrite a bit your code all_layers_catalog = function(las)
{
las = filter_poi(las, Classification != LASNOISE)
if (lidR::is.empty(las)) return(NULL)
las = normalize_height(las, knnidw(k = 8, p = 2))
las = filter_poi(las, Z < 50 & Z >= 1.37 )
if (lidR::is.empty(las)) return(NULL)
las <- decimate_points(las, random_per_voxel(res = 1, n = 8))
if (lidR::is.empty(las)) return(NULL)
### voxel metrics
las_vox <- ind_vox(las, res = 2)
vmetrics <- pixel_metrics(las_vox,
func = vox_sum_raster(SVi, FRDi, PDi, PDi_above, vox_res = 2),
res = 10)
return(vmetrics)
} The function can be applied raster <- catalog_map(ctg, all_layers_catalog) Now let apply las = readLAS(LASfile)
res1 = all_layers_catalog(las)
las$Z[1] = 45
res2 = all_layers_catalog(las)
plot(res1)
plot(res2) You can observe that changing the global maximum affects all the pixels including those that are very far from the global maximum point. To make it simple, your function does not compute a metric per pixel that is a function of the local points in the pixels, but that is also a function of the global point maximum in the point cloud. Your function does not compute valid metrics and the pixel values are potentially affected by an outlier 1 km further. When processing by chunk, you reduced the global view, get a different global maximum for each chunk and thus your values are irrelevant and not related. This is why you can see the edges. |
Beta Was this translation helpful? Give feedback.
-
Hello,
I have been struggling with applying custom voxel functions to a catalog. When I apply them (only on a catalog), differences appear in some of the chunks (see attached image). This does not happen when applying .std_metrics. I'm not looking for anyone to debug all the code (shown below), but I am throwing it out there in case anyone can tell right off the bat why applying a voxel function to a catalog creates different ranges of those metrics in each chunk.
Any thoughts are much appreciated!!!
The result regardless of what data I use looks something like below. Note the clear chunk outlines in the yellow oval:
Beta Was this translation helpful? Give feedback.
All reactions