CspStandSegmentation
An R-package for the segmentation of single trees from forest point clouds scanned with terrestrial, mobile or unmanned LiDAR systems.
https://github.com/julfrey/cspstandsegmentation
Category: Biosphere
Sub Category: Forest Remote Sensing
Last synced: about 12 hours ago
JSON representation
Repository metadata
R package for single tree delination from TLS/MLS/ULS point clouds
- Host: GitHub
- URL: https://github.com/julfrey/cspstandsegmentation
- Owner: JulFrey
- License: gpl-3.0
- Created: 2022-10-05T15:26:34.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2025-02-20T10:18:06.000Z (2 months ago)
- Last Synced: 2025-04-04T00:03:52.117Z (27 days ago)
- Language: R
- Size: 4.06 MB
- Stars: 18
- Watchers: 3
- Forks: 0
- Open Issues: 0
- Releases: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
README.md
CspStandSegmentation is an R-package for the segmentation of single trees from forest point clouds scanned with terrestrial, mobile or unmanned LiDAR systems
Authors: Julian Frey and Zoe Schindler, University of Freiburg, Chair of Forest Growth and Dendroecology
Installation
If you are working on Windows operating systems, you will need to install Rtools prior to installation: https://cran.r-project.org/bin/windows/Rtools/>. On Mac, Xcode is required.
install.packages(c('devtools', 'Rcpp', 'lidR', 'dbscan', 'igraph', 'foreach', 'doParallel','magrittr', 'data.table'))
devtools::install_github('https://github.com/JulFrey/CspStandSegmentation')
# Check if it is working
library(CspStandSegmentation)
example("csp_cost_segmentation")
Usage
The package is firmly based on the lidR
package and uses the las file structure. Smaller point clouds can be directly segmented using the csp_cost_segmentation
function. This requires a set of tree positions (map) as starting points, which can be derived using the find_base_coordinates_raster
function, which might require parameter optimization. Theoretically, tree positions might also come from field measurements or manual assignments.:
# read example data
file = system.file("extdata", "beech.las", package="CspStandSegmentation")
tls = lidR::readTLSLAS(file)
# find tree positions as starting points for segmentation
map <- CspStandSegmentation::find_base_coordinates_raster(tls)
# segment trees
segmented <- tls |>
CspStandSegmentation::add_geometry(n_cores = parallel::detectCores()/2) |>
CspStandSegmentation::csp_cost_segmentation(map, 1, N_cores = parallel::detectCores()/2)
# show results
lidR::plot(segmented, color = "TreeID")
# create inventory
inventory <- CspStandSegmentation::forest_inventory(segmented)
head(inventory)
lidR::plot(segmented, color = "TreeID") |> CspStandSegmentation::plot_inventory(inventory)
For large areas, the package can be used within the lidR LAScatalogue engine to cope with memory limitations. The following example shows how this can be done. The single tiles of segmented trees are saved in a folder in this example and merged afterwards.
# packages
library(lidR)
library(CspStandSegmentation)
# parameters
las_file <- "your_file.laz"
base_dir <- "~/your_project_folder/" # with trailing /
cores <- parallel::detectCores()/2 # number od cpu cores
res <- 0.3 # voxel resolution for segmentation
chunk_size <- 50 # size of one tile in m excl. buffer
chunk_buffer <- 10 # buffer around tile in m
# main
# create dir for segmentation tiles
if(!dir.exists(paste0(base_dir,"segmentation_tiles/"))) {
dir.create(paste0(base_dir,"segmentation_tiles/"))
}
uls = lidR::readTLSLAScatalog(paste0(base_dir,las_file), select = "XYZ0", chunk_size = chunk_size, chunk_buffer = chunk_buffer)
plot(uls, chunk_pattern = T)
# plot(dtm,add = T)
# sf::as_Spatial(sf::st_as_sf(map, coords = c("X", "Y"))) |> plot(add = T)
opt_output_files(uls) <- paste0(base_dir,"segmentation_tiles/{ID}")
segmented <- catalog_apply(uls, function(cluster) {
las <- suppressWarnings(readLAS(cluster)) # read files
if (is.empty(las) ) return(NULL) # stop if empty
print(str(cluster))
# find tree positions as starting points for segmentation
map <- CspStandSegmentation::find_base_coordinates_raster(las)
# add the tile ID*100,000 to the TreeID to ensure unique IDs across all tiles
map$TreeID <- map$TreeID + as.numeric(basename(cluster@save)) * 100000
# only use seed positions within the tile+buffer and save the tile bbox to only return tree pos within the tile (excl. buffer)
inv <- map
invb <- map
# the bbox includes the buffer, the bbbox excludes the buffer
bbox <- cluster@bbox
bbbox <- cluster@bbbox
inv <- inv[inv$X < bbox[1,2] & inv$X > bbox[1,1] & inv$Y < bbox[2,2] & inv$Y > bbox[2,1],]
invb <- invb[invb$X < bbbox[1,2] & invb$X > bbbox[1,1] & invb$Y < bbbox[2,2] & invb$Y > bbbox[2,1],]
if (nrow(inv) == 0) return(NULL) # stop if no tree pos in tile found
if (is.empty(las) ) return(NULL) # stop if empty
# Assign all points to trees
las <- las |> add_geometry(n_cores = cores) |> csp_cost_segmentation(invb,res, N_cores = cores, V_w = 0.5)
# las <- las |> csp_cost_segmentation(map,res, N_cores = cores, V_w = 0.5) # this is a faster version which does not make use of the geometric feature weights
if (is.empty(las)) return(NULL)
las <- las |> filter_poi(TreeID %in% c(0,inv$TreeID)) # only return trees within the tile
if (is.empty(las)) return(NULL) # stop if empty
# remove unneccesary attributes for further processing
las <- las |> remove_lasattribute('x_vox') |>
remove_lasattribute('y_vox') |>
remove_lasattribute('z_vox') |>
remove_lasattribute('buffer') |>
remove_lasattribute('Linearity') |>
remove_lasattribute('Sphericity') |>
remove_lasattribute('Verticality')
# validate las
las <- las |> las_quantize() |> las_update()
if (is.empty(las)) return(NULL)
return(las)
}, .options = list(automerge = TRUE))
# merge segmented trees
segmented <- readTLSLAScatalog(paste0(base_dir,"segmentation_tiles/"), select = "xyz0", chunk_buffer = 0)
opt_merge(segmented) <- TRUE
opt_output_files(segmented) <- paste0("")
segmented <- catalog_apply(segmented, function(cluster) {
las <- suppressWarnings(readLAS(cluster)) # read files
if (is.empty(las) ) return(NULL) # stop if empty
return(las)
}, .options = list(automerge = TRUE))
# write results in a single file
writeLAS(segmented, paste0(base_dir,"segmented.las"))
Owner metadata
- Name:
- Login: JulFrey
- Email:
- Kind: user
- Description:
- Website:
- Location:
- Twitter:
- Company:
- Icon url: https://avatars.githubusercontent.com/u/45684330?v=4
- Repositories: 1
- Last ynced at: 2023-04-25T12:49:42.088Z
- Profile URL: https://github.com/JulFrey
GitHub Events
Total
- Release event: 1
- Watch event: 6
- Push event: 14
- Create event: 1
Last Year
- Release event: 1
- Watch event: 6
- Push event: 14
- Create event: 1
Committers metadata
Last synced: 2 days ago
Total Commits: 58
Total Committers: 4
Avg Commits per committer: 14.5
Development Distribution Score (DDS): 0.345
Commits in past year: 33
Committers in past year: 2
Avg Commits per committer in past year: 16.5
Development Distribution Score (DDS) in past year: 0.333
Name | Commits | |
---|---|---|
JulFrey | j****y@i****e | 38 |
Julian Frey | 4****y | 15 |
Zoe | 5****r | 3 |
unknown | J****n@I****t | 2 |
Committer domains:
Issue and Pull Request metadata
Last synced: 1 day ago
Total issues: 1
Total pull requests: 2
Average time to close issues: 25 days
Average time to close pull requests: about 8 hours
Total issue authors: 1
Total pull request authors: 1
Average comments per issue: 4.0
Average comments per pull request: 0.0
Merged pull request: 2
Bot issues: 0
Bot pull requests: 0
Past year issues: 0
Past year pull requests: 0
Past year average time to close issues: N/A
Past year average time to close pull requests: N/A
Past year issue authors: 0
Past year pull request authors: 0
Past year average comments per issue: 0
Past year average comments per pull request: 0
Past year merged pull request: 0
Past year bot issues: 0
Past year bot pull requests: 0
Top Issue Authors
- LY0025 (1)
Top Pull Request Authors
- zoeschindler (2)
Top Issue Labels
Top Pull Request Labels
Dependencies
- Rcpp >= 1.0.5 imports
- TreeLS * imports
- data.table * imports
- dbscan * imports
- doParallel * imports
- foreach * imports
- igraph * imports
- lidR * imports
- magrittr * imports
- parallel * imports
- sf * imports
- terra * imports
Score: 4.276666119016055