-
Notifications
You must be signed in to change notification settings - Fork 10
Expand file tree
/
Copy pathsideview.py
More file actions
293 lines (231 loc) · 8.98 KB
/
Copy pathsideview.py
File metadata and controls
293 lines (231 loc) · 8.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 18 08:55:35 2023
@author: Zoe
"""
# import packages
import numpy as np
# import matplotlib.pyplot as plt
from sklearn.cluster import DBSCAN
import pandas as pd
def points_to_images(points, res_im = 256, num_side = 4, plot = False,
max_n = 500000, debug = False):
"""
Parameters
----------
points : np.array
XYZ point coordinates in np.array.
res_im : int, optional
Edge length of the quadratic tensor. The default is 256.
num_side : int, optional
number of side views. The default is 4.
plot : bool, optional
Plot the results for debugging. The default is False.
max_n: int, optional
Maximum number of points to be used for side- & topviews. The default
is 500,000.
Returns
-------
views : np.array
Different 2D views stacked in np.array.
"""
# # read in las file
# las = rl.read_las(path_las)
# prepare view array
views = np.zeros((num_side + 3, res_im, res_im), dtype = "float32")
# add DBH section view
# TODO: remove background points from section view
if debug: print('create dbh section view')
views[num_side + 2,:,:] = sectionview(points, res_im = res_im, plot = plot, debug = debug)
# subsample points to max_n points
if points.shape[0] > max_n:
points = points[np.random.choice(np.arange(points.shape[0]), max_n, replace = False),:]
# center point cloud
if debug: print('center point cloud')
points = points - np.median(points, axis = 0)
# scale point cloud using the maximum axis
if debug: print('scale point cloud')
points = points / np.max(np.abs(points))
# add top view
if debug: print('create top view')
views[0,:,:] = topview(points, res_im = res_im, plot = plot)
# loop through perspectives
deg_steps = np.arange(0, 360, 360/num_side)
# deg_steps = np.linspace(0, 180, num = num_side)
for i in range(num_side):
if debug: print('sideview: ' + str(i))
# get required rotation
deg = deg_steps[i]
# z rotation matrix
rad = np.radians(deg)
rot = np.array([
[np.cos(rad), -np.sin(rad), 0],
[np.sin(rad), np.cos(rad), 0],
[ 0, 0, 1]])
# rotate point cloud
points_rot = np.matmul(points, rot.T)
# get side images
views[i+1,:,:] = sideview(points_rot, res_im = res_im, plot = plot)
# add bottom view
if debug: print('bottom view')
views[num_side + 1,:,:] = topview(points, res_im = res_im, inverse = True, plot = plot)
# return
return views
# creating topview
def topview(points, res_im = 256, inverse = False, plot = False):
"""
Parameters
----------
points : np.array
XYZ point coordinates in np.array.
res_im : int, optional
Edge length of the quadratic tensor. The default is 256.
inverse : bool, optional
Calculate bottom view instead. The default is False.
plot : bool, optional
Plot the results for debugging. The default is False.
Returns
-------
top_image : np.array
2D view in np.array.
"""
# find the minimum and maximum values of the x, y, and z coordinates
x_min, y_min = np.min(points[:,[0,1]], axis = 0)
x_max, y_max = np.max(points[:,[0,1]], axis = 0)
x_med, y_med = np.median(points[:,[0,1]], axis = 0)
# create an empty numpy array to store the depth image
top_image = np.ones((res_im, res_im)) * -999
# calculate the size of each pixel in the x and y dimensions
size = max(x_max - x_min, y_max - y_min) / res_im
# determine longer axis
max_axis_x = (x_max - x_min) > (y_max - y_min)
# calculate image coordinates
if inverse:
if max_axis_x:
x_pos = np.array((points[:,0] - x_min) / size, dtype = int)
y_pos = np.array((points[:,1] - y_med) / size, dtype = int) + int(res_im/2)
else:
x_pos = np.array((points[:,0] - x_med) / size, dtype = int) + int(res_im/2)
y_pos = np.array((points[:,1] - y_min) / size, dtype = int)
else:
if max_axis_x:
x_pos = np.array((points[:,0] - x_min) / size, dtype = int)
y_pos = np.array((points[:,1] - y_med) / size, dtype = int) + int(res_im/2)
else:
x_pos = np.array((points[:,0] - x_med) / size, dtype = int) + int(res_im/2)
y_pos = np.array((points[:,1] - y_min) / size, dtype = int)
# save as pandas array
# wrap indices to avoid out of range indexing
points = pd.DataFrame({
"x": x_pos % res_im,
"y": y_pos % res_im,
"depth": points[:,2]})
# get minimum/maximum depth at each unique coordinate
if inverse:
points = points.groupby(["x", "y"])["depth"].min().reset_index()
else:
points = points.groupby(["x", "y"])["depth"].max().reset_index()
# overwrite depth values
top_image[points["x"], points["y"]] = points["depth"]
# replace dummy values with value
top_image[top_image == -999] = 0
# # show image
# if plot:
# plt.imshow(top_image, interpolation = 'nearest')
# plt.show()
# return array
return top_image
# creating sideview
def sideview(points, res_im = 256, plot = False):
"""
Parameters
----------
points : np.array
XYZ point coordinates in np.array.
res_im : int, optional
Edge length of the quadratic tensor. The default is 256.
plot : bool, optional
Plot the results for debugging. The default is False.
Returns
-------
side_image : np.array
2D view in np.array.
"""
# find the minimum and maximum values of the x, y, and z coordinates
x_min, z_min = np.min(points[:,[0,2]], axis = 0)
x_max, z_max = np.max(points[:,[0,2]], axis = 0)
x_med, z_med = np.median(points[:,[0,2]], axis = 0)
# create an empty numpy array to store the depth image
side_image = np.ones((res_im, res_im)) * -999
# calculate the size of each pixel in the x and y dimensions
size = max(x_max - x_min, z_max - z_min) / res_im
# determine longer axis
max_axis_x = (x_max - x_min) > (z_max - z_min)
# calculate image coordinates
if max_axis_x:
x_pos = np.array((points[:,0] - x_min) / size, dtype = int)
z_pos = np.array((points[:,2] - z_med) / size, dtype = int) + int(res_im/2)
else:
x_pos = np.array((points[:,0] - x_med) / size, dtype = int) + int(res_im/2)
z_pos = np.array((points[:,2] - z_min) / size, dtype = int)
# save as pandas array
# wrap indices to avoid out of range indexing
points = pd.DataFrame({
"x": x_pos % res_im,
"z": z_pos % res_im,
"depth": points[:,1]})
# get maximum depth at each unique coordinate
points = points.groupby(["x", "z"])["depth"].max().reset_index()
# overwrite depth values
side_image[points["x"], points["z"]] = points["depth"]
# replace dummy values with value
side_image[side_image == -999] = 0
# # show image
# if plot:
# plt.imshow(side_image, interpolation = 'nearest')
# plt.show()
# return array
return side_image
# creating sideview
def sectionview(points, res_im = 256, plot = False, debug = False):
"""
Parameters
----------
points : np.array
XYZ point coordinates in np.array.
res_im : int, optional
Edge length of the quadratic tensor. The default is 256.
plot : bool, optional
Plot the results for debugging. The default is False.
Returns
-------
section_image : np.array
2D view in np.array.
"""
# extract DBH section
section = points[(points[:,2] < 1.5) & (points[:,2] > 1),:]
if debug: print('n points in section section: ' + str(section.shape[0]))
# skip if section nearly empty
if section.shape[0] > 50:
# set up the clustering algorithm
dbscan = DBSCAN(eps = 0.10, min_samples = 10)
# fit the clustering algorithm
dbscan.fit(section)
# get largest cluster
labels = dbscan.labels_
# catch if no cluster > 10 points is found
if not (np.array(labels) == -1).all():
largest_cluster_label = np.argmax(np.bincount(labels[labels!=-1]))
largest_cluster_indices = np.where(labels == largest_cluster_label)[0]
section = section[largest_cluster_indices]
# center point cloud
section = section - np.median(section, axis = 0)
# scale point cloud
section = section / np.max(np.abs(section))
# create sideview
section_image = sideview(section, res_im = res_im, plot = plot)
else:
# create empty array
section_image = np.zeros((res_im, res_im))
# return array
return section_image