UnfortunatleyUnfortunately this code produces worse results than pdal
.
The .las
file for testing is available here.
Edit:
Combining the above code with the individual tree segmentation of lidR gives good results.
require(lidR)
las = readLAS("test.las")
las = lasground(las, csf())
algo = pitfree(thresholds = c(0,10,20,30,40,50), subcircle = 0.2)
chm = grid_canopy(las, 0.5, algo)
algo = watershed(chm, th = 4)
las = lastrees(las, algo)
writeLAS(las, "tmp2.las")
For each point classified to be part of a tree by lidR I vote if this is a tree or ground by taking a weighted average of the result of the nearest neighbor python code. This is done in the following python code (tmp.las
is the output of the nearest neighbor code).
import numpy as np
import pylas
from collections import defaultdict
with pylas.open("tmp.las") as f:
x = f.read()
with pylas.open("tmp2.las") as f:
y = f.read()
xx = np.array([x[v] for v in ("X", "Y", "Z", "intensity")]).transpose().copy()
yy = np.array([y[v] for v in ("X", "Y", "Z", "intensity")]).transpose().copy()
valid_points = []
ii = 0
for i in range(len(x.points)):
if np.count_nonzero(xx[i] - yy[ii]) == 0:
valid_points.append(x.points[i])
ii += 1
if ii > len(yy): break
assert len(valid_points) == len(yy)
x.points = np.array(valid_points, dtype=x.points.dtype)
treeID = y["treeID"]
classification = x["classification"]
ground = y["classification"] == 2
istree = (-0.25 * (classification == 2) +
1 * (classification == 3) +
0 * (classification == 1))
d = defaultdict(int)
for i in range(len(x.points)):
d[treeID[i]] += istree[i]
for i in range(len(y.points)):
if d[treeID[i]] > 0 and not ground[i]:
classification[i] = 3
else:
classification[i] = 2
y["classification"] = classification
y.write("tmp3.las")
Still there are some trees which are classified as ground. I suspect the reason is that the treeID
of lidR is an 8 bit integer and there seems to be more than 256 trees.