If you don’t already have the input data, HERE it is. The clip layer is here: CLIPLAYER.
Now that you have a good idea of how the various parameters affect the final output, I have modified the code so that we input the parameters near the beginning of the code (starting at line 30) and run the entire process. Output images, shapefiles, and informational text files are differentiated using the myRunNumber variable.
Insert your better parameters and run the script. Compare the tree top and crown delineation shapefiles (output on lines 73 and 84, repsectively) to the orthophoto. Is your classification better? How? Can you make it more better?
#####https://rdrr.io/cran/lidR/ #####install libraries if not already #####install.packages("lidR") #####install.packages("raster") #####install.packages("sp") #####install.packages("gstat") #####install.packages("dplyr") library(lidR) library(raster) library(sp) library(rgdal) myfile<- "clpSG_PointCloud_th001.las" inpath<- "c:/projects/week8"# #####create folder for outputs outfolder<- "outputs2" if (file.exists(file.path(inpath,outfolder))){ } else { dir.create(file.path(inpath,outfolder)) } outpath<- file.path(inpath,outfolder) mylas<- readLAS(file.path(inpath,myfile)) # Extract a polygon from a shapefile lakes = rgdal::readOGR(file.path(inpath), "clplayer") lake = lakes@polygons[[1]]@Polygons[[1]] subset_mylas = lasclip(mylas, lake) #####PARAMETER LIST myRunNumber<- "2" myThreshhold<- 0.05 myResolution<- 0.5 myRigidness<- 3 myK<- 100 myPits<- c(0,.1, .2, 0.3, 0.5, 0.75, 1, 2, 5, 10) myC<- c(0, 1, 1.5, 2) myLMFMovingWindow<- 1 myLMFHmin<- 0.1 #####GROUND CLASSIFICATION OF POINTS; NEW OBJECT 'lasgrnd' lasgrnd<- classify_ground(subset_mylas, csf( sloop_smooth = FALSE, class_threshold = myThreshhold, cloth_resolution = myResolution, rigidness = myRigidness ) ) #####OUTPUT DTM RASTER dtm<- grid_terrain(lasgrnd,res = myResolution, algorithm = knnidw(k=myK, p=2), keep_lowest = TRUE) outName<- paste("run",myRunNumber,"_myDTM_res",gsub("\\.","",toString(myResolution)),"_theK",myK,".tif",sep="") writeRaster(dtm,filename=file.path(outpath,outName), overwrite=TRUE) #####OUTPUT DSM RASTER dsm<- grid_canopy(lasgrnd, res=myResolution, algorithm=pitfree(myPits, myC)) outName<- paste("run",myRunNumber,"myDSM_res",gsub("\\.","",toString(myResolution)),".tif",sep="") writeRaster(dsm, filename=file.path(outpath,outName), overwrite=TRUE) #####NORMALIZE POINT CLOUD; CREATE NEW OBJECT 'lasnorm' lasnorm<- normalize_height(lasgrnd, knnidw(k=myK, p=2)) #####filter points less than 0 lasnorm<- filter_poi(lasnorm, !(lasnorm$Z < 0)) #####CREATE CANOPY HEIGHT MODEL; SAME AS DSM BUT WITH NORMALIZED POINT CLOUD chm <- grid_canopy(lasnorm, res = myResolution, pitfree(myPits,myC)) kernel<- matrix(1,3,3) chm3a = raster::focal(chm, w=kernel, fun=mean) chm3a = raster::focal(chm, w=kernel, fun=mean) #####FIND TREE TOPS; EXPORT POINT SHAPEFILE ttops<- find_trees(lasnorm,lmf(ws=myLMFMovingWindow, hmin = myLMFHmin)) myOutputShapefileName<- paste("run",myRunNumber,"_ttops",sep="") writeOGR(ttops,dsn=outpath,layer=myOutputShapefileName, driver="ESRI Shapefile", overwrite_layer=TRUE) #####CROWN SEGMENTATION crowns<- segment_trees(lasnorm,dalponte2016(chm3a,ttops,th_tree=myLMFHmin)) #####DELINEATE TREE CROWNS hulls<- delineate_crowns(crowns, func = ~list(Zmean = mean(Z))) #####OUTPUT CROWNS; EXPORT SHAPEFILE myOutputShapefileName<- paste("run",myRunNumber,"_crowns",sep="") writeOGR(hulls,dsn=outpath,layer=myOutputShapefileName, driver="ESRI Shapefile", overwrite_layer=TRUE) #####OUTPUT TEXT FILE WITH RUN # AND PARAMETERS myOutputInformaitonfileName<- paste("run",myRunNumber,"_information",sep="") myParameters<- NULL myParameters<- data.frame(myRunNumber, myThreshhold, myResolution, myRigidness, myK, gsub("\\,","",toString(myPits)) , gsub("\\,","",toString(myC)), myLMFMovingWindow, myLMFHmin ) names(myParameters)<- c("myRunNumber","myThreshhold","myResolution","myRigidness","myK", "myPits","myC","myLMFMovingWindow","myLMFHmin") write.csv(myParameters,file.path(outpath,paste(myOutputInformaitonfileName,".txt",sep="")))
Check out your tree crown polygon attribute table. It is already populated with the tree apex height, ZTop, and mean height, Zmean, from line 85. Where are the blunders occurring? Can you use this information for one final refinement?
Now that you have generated a better crown delineation. I want you to do the following:
- Use screenshots and text to demonstrate and describe the effects of the various parameters on the tree crown delineation. Present output from a run using a bad parameter (or set of parameters) and then using your better parameters.
- Demonstrate for:
- grid_terrain (line 51)
- normalize_height (line 61)
- grid_canopy (line 66)
- find_trees (line 72)
- segment_trees (line 77)
- A Power Point slide with screenshots and text are fine.