2019.12.09
处理数据:研究区LiDAR点云数据
1.去除离群值Remove Outlier,使用CloudCompare软件Tools→Clean→Noise Filter
去噪参数有k Nearest Neighbor,Radius;相对最大误差和绝对最大误差。得到Cloud.clean导出至点云。
2.使用R package lidR进行点云分类lasfilters,将点云分为冠层点云与地面点云
library('lidR')
lidar_denoised = readLAS('Process/2.NoiseFilter/LiDAR_Denoised.las')
firstReturns = lasfilterfirst(lidar_denoised)
groundReturns = lasfilterground(lidar_denoised)
plot(lidar_denoised)
3.接着生成DTM(Digital Terrain Model),代码如下:
dtm <- grid_terrain(lidar_denoised, 1, kriging(k = 10L))
lidar_denoised <- lasnormalize(lidar_denoised, dtm)
plot(dtm)
plot(lidar_denoised)
但是发生报错:
No ground points found. Impossible to compute a DTM.
2019.12.10
发现还是需要先对点云.las进行分类,想了一下用什么方法进行点云分类,因为看了一下R的lidR package里面好像没有一个明晰的能够对.las文件进行groundpoint分类的函数,都是输出到一个新的las格式文件里面的,这样相当于clip出来了,是单独的。
于是这里我查找了一下相关的软件:
- Pix4D,发现只能用于针对原始的Digital Arial Photograph进行点云生成在进行分类(不适用LiDAR产品)。
- CloudCompare,倒是可以进行分类,不过同R,输出的是两个文件ground point和off-ground point,而且它用的很多处理都是Plugin,就是
- ENVI LiDAR,可以很方便的生成tree,building以及DTM,DSM的产品,但是不能分类。
- LasTool,之前用过ArcGIS版本的工具包,但是因为版本不够正的原因,总是直接未响应退出,虽然具有Classify的功能,在这里就未做尝试了
- TerraSolid,尝试一下。
好的TerraSolid它来了,但是TerraSolid一般这个软件本身是用来处理3D模型数据的(包括.obj等格式),这个时候就用到了它的插件TerraScan。然后开启,就可以导入.las数据。
然后它挺快就自己运行完了,虽然没有进度条什么的,我还以为要运行1h左右。然后再输出所有分类好的点就可以了。
2019.12.11
接着再进行Remove Outlier&Filter,重复之前的步骤。
现在再整合一下顺序应该是:
1.LiDAR的.las文件用TerraSolid的插件TScan进行点云分类,分类出地面点;如果是用Pix4D处理的Digital Arial Photograph(DAP)影像,直接生成点云后进行分类。
2.使用CloudCompare软件进行滤波去噪。
3.使用R的lidR package,生成DTM并进行点云归一化。
library('lidR')
lidar = readLAS('2.NoiseFilter/LiDAR_Classified_Denoised.las')
dtm <- grid_terrain(lidar, 1, kriging(k = 10L))
lidar_normalized <- lasnormalize(lidar, dtm)
好的,现在预处理步骤已经完成了,可以写一个.las文件对比看看归一化前后的结果。本来是用R直接plot的,但是由于一plot就卡住不动了还是用CloudCompare看看可视化。
writeLAS(lidar_normalized,'3.Normalization/lidar_normalized.las')
但是突然发现归一化之后的点云,还是有很多离群值。也就是CloudCompare的Noise Filter并没有很好的去除Outliers,于是这里又重新用R的lidR里面的smooth函数试了一下。
lidar_smoothed <- lassmooth(lidar, 5, "gaussian", "circle", sigma = 2)
然后,我接着生成DTM,然后进行归一化。还是发现结果有许多离群点云,这个时候应该粗暴一点,先归一化后Smooth。顺序处理代码如下:
library('lidR')
lidar = readLAS('2.NoiseFilter/LiDAR_Classified_Denoised.las')
dtm <- grid_terrain(lidar, 1, kriging(k = 10L))
writeRaster(dtm,'3.Normalization/DTM.tiff',format='GTiff')
lidar_normalized <- lasnormalize(lidar, dtm)
writeLAS(lidar_normalized,'3.Normalization/lidar_normalized.las')
lidar_smoothed <- lassmooth(lidar_normalized, 5, "gaussian", "circle", sigma = 2)
writeLAS(lidar_smoothed,'3.Normalization/lidar_smoothed_normalized.las')
然后下面是Normalization的结果,但是似乎还是有一点脱离整体的点云。
4.使用lastrees函数对归一化之后的点云进行单木分割
Tree segmentation: 57% (1 threads)=======================================] 100% (1 threads)
真的跑了很久,这里的.las文件是3.3G。我的电脑是24G内存,i7的7代CPU,感觉大概跑2-3个小时左右。在RStudio中,使用plot()语句进行点云的显示,尤其是非常大的点云数据,是一个错误的选择。因为数据量大导致电脑一段时间内都很卡,不如写出.las文件在可视化软件中打开。
5.加载GPS点,对应已有的样方内的树所在的位置,调整单木分割参数。
发现一个严重的问题R在读取.las的时候会把它的proj4string丢了,也可能一开始就没得,但是emmm现在不知道怎么找回来。
然后,试试以下的方法是否有用。
library('maptools')
GPS<-st_read('GPS/GPS.shp')
出现,复制proj4string中的内容,赋值给.las或者rasterlayer的dtm。
Reading layer `GPS' from data source `E:\1.COURSES\1.Graduate_Design\Process\GPS\GPS.shp' using driver `ESRI Shapefile'
Simple feature collection with 394 features and 42 fields
geometry type: POINT
dimension: XYZ
bbox: xmin: 245517.4 ymin: 3375919 xmax: 245974.6 ymax: 3376504
epsg (SRID): 4326
proj4string: +proj=longlat +datum=WGS84 +no_defs
proj4string(dtm)<-CRS("+proj=longlat +datum=WGS84 +no_defs")
proj4string(lidar)<-CRS("+proj=longlat +datum=WGS84 +no_defs")
后面需要学习一下R中shp矢量文件的导入和显示。
2019.12.15
终于明白这个Shapefile在RStudio中如何显示了,网上很多的都用到了“maptools”这个package去使用函数readShapeSpatial(),但是实际上这个函数已经被取代了,于是就用了以下的方法来显示。
library("maptools")
library('rgdal')
GPS<-readOGR('GPS/GPS_counterpart.shp')
plot(GPS)
得到的显示结果如图所示:
又发现了一个严重的问题:导出的.las数据是没有坐标系和投影的,但是却有坐标范围;但是GPS是定义了坐标系的。于是就导致了如下的问题:
2019.12.16
终于对齐了!RTK的地理坐标是WGS84,但是在ArcMap中导入XY的时候需要输入的是经纬度,而不是转换成投影坐标之后的XY,然后再使用“投影”工具投影到UTM 50N上,就可以与LiDAR数据对其了。
啊哈,又拿到了数据,但是得从头开始做。
开始用Lastool..
1.lasmerge,合并两块LiDAR点云
2.lasnoise,去除噪声
3.lasground,将点云分类出地面点
2019.12.17
4.导入RStudio,生成DTM
5.使用lasnormalize,对点云进行归一化
6.发现归一化之后的点云出现许多的outlier的部分(其实这里感觉是lidR package中对DTM的算法或者normalize的算法或者参数有点问题的,但是具体的问题需要再进一步探索)。
7.这里不得不先用smooth函数对点云进行平滑,设置的窗口比较大,但是谁想到smooth跑了一整天大约10个小时。顶不住了,感觉得用一下TerraSolid的插件了。
2019.12.18
最近的实验记录逐渐敷衍...
好的,今天主要还是用了ENVI LiDAR进行了DTM,DSM,building,tree的生成,结果还不错,但是现在需要对GPS点进行修正。
2019.12.19
现在的主要问题是要进行单木分割,主要用R进行单木分割,ENVI LiDAR也可以生成Canopy的种子点。并且如果要以修正过的样方为单位的话,现在的参数就变为了:
- Lorey's mean height:在一个样方中,所有树木的Height×Basal Area相加,再除以Basal Area的总值。其实这个直接在实地数据采集Excel表格里面进行计算也可以。
- Diameter at Breast Height:总的来说是胸径,即1.3m处高得到的树木周长拟合为圆算得的大约直径。
- Basal Area:胸径断面积,就是.
- Gap Fraction:森林孔隙率,当然这个是拿鱼眼相机拍了之后用专业软件处理得到的一个百分比的值,之后还要琢磨一下怎么去代表一个样方。
-
LAI:叶面积指数,也是用LAI2000这个仪器测得的,也需要琢磨一下代表样方的方式,虽然很想和上者一样直接求平均或者插值,但是可能具体的还是要看文献。
好的再来写个Plan:
1.先把实地数据处理出来,比如每个样方的以上5个参数;
2.然后把LiDAR数据中的每个样方的metrics找出来;
3.然后进行模型建立与拟合。
2019.12.23
好的,今天终于又活过来了。
1.计算好了18个样方的Lorey's Mean Height:;
2.计算好了Basal Area,单位是;
3.根据公式(可能是一个锥度方程,简单的Standing tree volume的计算方法都是用这个计算的,一般在美国量算这个的单位为BF,板尺,1BF=144立方英寸,奇妙)
同样也是计算了18个样方中所有树Volume的合;
4.计算代表样方的Gap Fraction与LAI(Leaf Area Index)。
目前就有针对性的处理Gap Fraction,首先需要一个相机+鱼眼镜头(型号:NIKON D610+Sigma EX DG 8mm),在距离地面约1.3m的高度水平拍摄相片,用HemiView软件进行处理。
找到了对于一个样方中Gap Fraction的计算方法,在看了很多英文文献之后都没有找到想到的对于代表一个样方的Gap Fraction的计算方案,大多是对于这个间隙分数的反演方法的研究。之后又尝试找了一篇硕士论文,发现求平均就可以。(=-=算是一个经验教训吧。)
2019.12.24
总算是把5个参数都计算完毕:
- Lorey's Mean Height(m)
- Basal Area()
- Volume()
- Gap Fraction
- Leaf Area Index(LAI2000直接测出来就有结果不需要后处理和计算啦!)
现在就是把LIDAR数据用.shp文件把18个样方给裁剪出来,然后计算metrics。
The first returns were significantly related to the target height, and according to previous studies, have proved to be effective for determining forest structural attributes (Kim et al., 2009, Ritchie, 1993). In our study, the UAV-LiDAR point cloud metrics were calculated from the first return, and three sets of metrics were computed: (1) distributional metrics, i.e., percentile heights (H25, H50, H75 and H95), mean height (Hmean), coefficient of variation of height (Hcv), kurtosis of heights (Hkurtosis), Interquartile distance of height, Variance of height (Hvariance), canopy return density (D3, D5 and D7) and canopy cover above 1.3 m (CC1.3 m) (Lim et al., 2003b, Næsset and Gobakken, 2008, Thomas et al., 2006); (2) canopy volume-related metrics, (i.e., Open gap (Open), Closed gap (Closed), Euphotic (E) and Oligophotic (O)) (Lefsky et al., 1999); (3) Weibull model -fitted related metrics, i.e., α (scale parameter)and β(shape parameter) (Coops et al., 2007); (4) The individual tree metrics,(i.e., maximum height (ITCmax), minimum height (ITCmin), mean height (ITCmean), variance in height (ITCcv) and stem density (ITCdensity)) (Table 3).
Liu, K.; Shen, X.; Cao, L.; Wang, G.; Cao, F. Estimating forest structural attributes using UAV-LiDAR data in Ginkgo plantations. ISPRS Journal of Photogrammetry and Remote Sensing 2018, 146, 465–482.
2019.12.25
先整理一下高度Attributes:
- 百分位数5,25,50,75,95,99
- 平均高度
- 变异系数
- 峰度、偏度
- Interquartile distance of height?翻译是四分位距离,这是虾米?
- 方差
2019.12.26
目前需要计算的:
- 百分位数5,25,50,75,95,99
- 平均高度
- 变异系数
- 峰度、偏度
- 标准差
这些都可以使用LAStools包中的lascanopy进行计算。
还需要进行计算的有: - 顶篷返回密度(D1,D3,D5,D7和D9),分位数(第10、30、50、70和90分)以上的点数占总点数的比例
- 顶篷覆盖高度超过1.3 m(CC 1.3m,感觉许多文章里面也有说2m的,不过为了一致性还是1.3m好了),首次返回超过1.3 m的点云的百分比
尝试用R中的metrics函数自行编写函数计算。
看来还是不太明白R中的数据格式的调用和遍历,上Google找了一下,找了一篇比较好的参考教程是关于‘rLiDAR’包的:https://www.researchgate.net/publication/324437694_LiDAR_Analysis_in_R_and_rLiDAR_for_Forestry_Applications
2018.4月份才出的要配上最新的3.6.2的R language,重新下一遍再搞一下试试。
幸好它出了,用Lastools算出来的Canopy Cover都是99%左右,我就奇了怪了。
2019.12.27
setwd("E:/1.COURSES/1.Graduate_Design/Process")
library('lidR')
lidar <- readLAS('4.Normalization/lidar_normalized.las')
library('shapefiles')
# read shapefile of each stand
shp <- list()
for(n in 1:18){
string <- paste('GPS/StandShapefiles/Stand',n,'.shp',sep = "", collapse = "")
shp[[n]] <- shapefile(string)
}
# do clip for each stand
lidar_subset <- list()
for (n in 1:18) {
lidar_subset[[n]] <- lasclip(lidar,shp[[n]])
}
# write lidar point cloud of each stand
for (n in 1:18) {
filepath <- paste('7.Subset/Stand',n,'.las',sep = "", collapse = "")
writeLAS(lidar_subset[[n]],filepath)
}
# calculate canopy cover
library('rLiDAR')
minht<-1.37
above<-2.00
LiDARmetrics <- list()
for (n in 1:18) {
standlas <- paste('8.Subset/LiDAR_Stand_las/Stand',n,'.las',sep = "", collapse = "")
LiDARmetrics<- rbind(LiDARmetrics,LASmetrics(standlas, minht, above))
}
write.csv(LiDARmetrics,'9.PointCloudAttributes/LiDAR_Metrics_cp.csv')
# calculate density percentile
# define calculate function
density_cal = function(z,percentile_height){
total = 0
count = 0
for (n in z) {
if (n > percentile_height) {
count = count+ 1
}
total = total + 1
}
density = count/total
return(density)
}
# read percentile height csv
height_percentiles <- read.csv('9.PointCloudAttributes/DensityPercentilesCal.csv')
# calculate density of percentiles(10,30,50,70,90) in each stand
density_result <- data.frame(order =c(1:18))
for (i in 1:5) {
density_col <- data.frame()
for (j in 1:18) {
density_col<- rbind.data.frame(density_col,density_cal(lidar_subset[[j]]$Z,height_percentiles[[j,i+1]]))
}
density_result<- cbind.data.frame(density_result,density_col)
}
colnames(density_result)<-c("order","D1","D3","D5","D7","D9")
write.csv(density_result,'9.PointCloudAttributes/LiDAR_density_metrics.csv')
我不行了,重新学了R的所有变量格式以及使用方法,真的比python反人类,将就看吧,主要是进行了Density的计算。
今日吐槽:为什么这些文献要讲这些乱七八糟看不懂的原理,为什么不能直接给我一个计算公式为什么?????!!!!!不懂!!
Canopy volume metrics related paper