Zelas2 1.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
zelas2-1.0.1/PKG-INFO ADDED
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.1
2
+ Name: Zelas2
3
+ Version: 1.0.1
4
+ Summary: Static LiDAR point cloud processing library
5
+ Author: Ze You
6
+ Author-email: 878054597@qq.com
7
+ Keywords: python,las,LiDAR,windows,linux
8
+ Classifier: Development Status :: 1 - Planning
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Operating System :: Unix
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Description-Content-Type: text/markdown
14
+
15
+
16
+ Hello,World
zelas2-1.0.1/README.md ADDED
@@ -0,0 +1 @@
1
+ Hello,World
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.1
2
+ Name: Zelas2
3
+ Version: 1.0.1
4
+ Summary: Static LiDAR point cloud processing library
5
+ Author: Ze You
6
+ Author-email: 878054597@qq.com
7
+ Keywords: python,las,LiDAR,windows,linux
8
+ Classifier: Development Status :: 1 - Planning
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3.8
11
+ Classifier: Operating System :: Unix
12
+ Classifier: Operating System :: Microsoft :: Windows
13
+ Description-Content-Type: text/markdown
14
+
15
+
16
+ Hello,World
@@ -0,0 +1,16 @@
1
+ README.md
2
+ setup.py
3
+ Zelas2.egg-info/PKG-INFO
4
+ Zelas2.egg-info/SOURCES.txt
5
+ Zelas2.egg-info/dependency_links.txt
6
+ Zelas2.egg-info/top_level.txt
7
+ zelas2/Ellipse.py
8
+ zelas2/Multispectral.py
9
+ zelas2/RedundancyElimination.py
10
+ zelas2/TheHeartOfTheMilitaryGod.py
11
+ zelas2/__init__.py
12
+ zelas2/dxf_dwg.py
13
+ zelas2/ransac.py
14
+ zelas2/setup.py
15
+ zelas2/shield.py
16
+ zelas2/voxel.py
@@ -0,0 +1 @@
1
+ zelas2
zelas2-1.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
zelas2-1.0.1/setup.py ADDED
@@ -0,0 +1,30 @@
1
+ import codecs
2
+ import os
3
+ from setuptools import setup, find_packages
4
+ # these things are needed for the README.md show on pypi (if you dont need delete it)
5
+ here = os.path.abspath(os.path.dirname(__file__))
6
+ with codecs.open(os.path.join(here, "README.md"), encoding="utf-8") as fh:
7
+ long_description = "\n" + fh.read()
8
+ # you need to change all these
9
+ VERSION = '1.0.1'
10
+ DESCRIPTION = 'Static LiDAR point cloud processing library'
11
+ LONG_DESCRIPTION = 'Produced by Professor Wang Liying team from the School of Surveying and Geographic Sciences, Liaoning University of Engineering and Technology'
12
+ setup(
13
+ name="Zelas2",
14
+ version=VERSION,
15
+ author="Ze You",
16
+ author_email="878054597@qq.com",
17
+ description=DESCRIPTION,
18
+ long_description_content_type="text/markdown",
19
+ long_description=long_description,
20
+ packages=find_packages(),
21
+ install_requires=[],
22
+ keywords=['python', 'las', 'LiDAR','windows','linux'],
23
+ classifiers=[
24
+ "Development Status :: 1 - Planning",
25
+ "Intended Audience :: Developers",
26
+ "Programming Language :: Python :: 3.8",
27
+ "Operating System :: Unix",
28
+ "Operating System :: Microsoft :: Windows",
29
+ ]
30
+ )
@@ -0,0 +1,554 @@
1
+ # coding=utf-8
2
+ import numpy as np
3
+ from ellipse import LsqEllipse
4
+ from scipy.optimize import minimize
5
+ import multiprocessing as mp # 添加多线程库
6
+ import zelas.Multispectral as p0
7
+ import math
8
+ """
9
+ 本源代码有两个功能:This source code has two functions:
10
+ 1:二维点拟合椭圆:fitellipse # 无法使用 2D point fitting ellipse: fitellipse # cannot be used
11
+ 2:点到椭圆的最近距离(后续可以将切点也找到)# 现已停止使用 :dis_ellipse The closest distance from a point to an ellipse (you can also find the tangent point later): dis_ Ellipses
12
+ 3: 点到椭圆的最近距离(无bug版)find_dis_mp:多线程版,block为非线程版
13
+ 注:其他类与函数为内部函数,无法对外使用 Note: Other classes and functions are internal functions and cannot be used externally
14
+ 已知bug:dis_ellipse的精度不能太高,否则计算会有误差 Known bug: dis_ The accuracy of ellipses should not be too high, otherwise there may be errors in the calculation
15
+ """
16
+
17
+
18
+ class CostFunction_ellipse3:
19
+ """Cost function for ellipse fit (x=[A B C D E]. Initialised with points."""
20
+
21
+ def __init__(self, pts):
22
+ self.pts = pts
23
+
24
+ def f(self, x1):
25
+ """Evaluate cost function fitting ellipse"""
26
+ x = self.pts[0, :]
27
+ y = self.pts[1, :]
28
+ x = x[:, np.newaxis]
29
+ y = y[:, np.newaxis]
30
+ x1 = x1[np.newaxis, :]
31
+ D = np.hstack((x * x, x * y, y * y, x, y, np.ones_like(x)))
32
+ # S = np.dot(D.T,D)
33
+ C = np.zeros([6, 6])
34
+ C[0, 2] = C[2, 0] = 2
35
+ C[1, 1] = -1
36
+ aa = np.dot(x1, D.T)
37
+ aa = np.dot(aa, D)
38
+ aa = np.dot(aa, x1.T)
39
+ bb = np.dot(x1, C)
40
+ bb = np.dot(bb, x1.T)
41
+ d = aa - bb
42
+ return np.sum(d)
43
+
44
+
45
+ def solve_ellipse(A, B, C, D, E, F):
46
+ Xc = (B * E - 2 * C * D) / (4 * A * C - B ** 2)
47
+ Yc = (B * D - 2 * A * E) / (4 * A * C - B ** 2)
48
+
49
+ FA1 = 2 * (A * Xc ** 2 + C * Yc ** 2 + B * Xc * Yc - F)
50
+ FA2 = np.sqrt((A - C) ** 2 + B ** 2)
51
+
52
+ MA = np.sqrt(FA1 / (A + C + FA2))
53
+ SMA = np.sqrt(FA1 / (A + C - FA2)) if A + C - FA2 != 0 else 0
54
+
55
+ if B == 0 and F * A < F * C:
56
+ Theta = 0
57
+ elif B == 0 and F * A >= F * C:
58
+ Theta = 90
59
+ elif B != 0 and F * A < F * C:
60
+ alpha = np.arctan((A - C) / B) * 180 / np.pi
61
+ Theta = 0.5 * (-90 - alpha) if alpha < 0 else 0.5 * (90 - alpha)
62
+ else:
63
+ alpha = np.arctan((A - C) / B) * 180 / np.pi
64
+ Theta = 90 + 0.5 * (-90 - alpha) if alpha < 0 else 90 + 0.5 * (90 - alpha)
65
+
66
+ if MA < SMA:
67
+ MA, SMA = SMA, MA
68
+ Theta = Theta * np.pi / 180 # 角度转弧度(matlab为默认弧度) Angle to radian (default radian in MATLAB)
69
+ return [Xc, Yc, MA, SMA, Theta]
70
+
71
+
72
+ def fitellipse(xy):
73
+ '点云拟合椭圆 Point cloud fitting ellipse'
74
+ shapes = xy.T # 数据转置 #Data transposition
75
+ '拟合椭圆'
76
+ e3 = CostFunction_ellipse3(shapes)
77
+ x2 = np.array([0.01] * 6, dtype='float64')
78
+ res1 = minimize(e3.f, x2, method='Powell')
79
+ '模型求解'
80
+ arg = solve_ellipse(res1.x[0], res1.x[1], res1.x[2], res1.x[3], res1.x[4], res1.x[5]) # 圆心、长短半轴、倾角 #Center, major and minor half axes, inclination angle
81
+ print('椭圆圆心为', arg[0], arg[1], '椭圆长短半轴为', arg[2], arg[3], '椭圆极角为', arg[-1])
82
+ return np.array(arg) # 返回[椭圆圆心x,椭圆圆心y,椭圆长半轴,椭圆短半轴,倾角] Returns [ellipse center x, ellipse center y, ellipse major half axis, ellipse minor half axis, inclination angle]
83
+
84
+
85
+ def solve_cubic_eq(a0, a1, a2):
86
+ '解一元三次函数'
87
+ np.set_printoptions(precision=17)
88
+ Q = (3 * a1 - a2 ** 2) / 9
89
+ R = (9 * np.multiply(a2, a1) - 27 * a0 - 2 * a2 ** 3) / 54
90
+ D = np.float_power(Q, 3) + np.float_power(R, 2) # 与matlab结果不一致
91
+ SD = np.emath.sqrt(D) # 负数平方根
92
+ S = np.zeros(len(R))
93
+ T = np.zeros(len(R))
94
+ i = np.where(D >= 0)[0] # 符合条件的下标
95
+ if len(i) > 0:
96
+ S[i] = pow((R[i] + SD[i]), (1 / 3))
97
+ T[i] = pow((R[i] - SD[i]), (1 / 3))
98
+ i = np.where(D < 0)[0]
99
+ if len(i) > 0:
100
+ S[i] = (R[i] + SD[i]) ** (1 / 3)
101
+ T[i] = (R[i] - SD[i]) ** (1 / 3)
102
+ z = np.array([-a2 / 3 + (S + T) / 2, -a2 / 3 - (S + T) / 2 + np.emath.sqrt(-3) * (S - T) / 2, -a2 / 3 - (S + T) / 2 - np.emath.sqrt(-3) * (S - T) / 2]).T
103
+ return z
104
+
105
+
106
+ def solve_quartic_eq(a0, a1, a2, a3):
107
+ '解一元四次方程组'
108
+ b2 = a2 * -1
109
+ np.set_printoptions(precision=14)
110
+ b1 = np.multiply(a1, a3) - 4 * a0 # 计算值正负号与matlab1有差距
111
+ b0 = 4 * np.multiply(a2, a0) - a1 ** 2 - np.multiply(a3 ** 2, a0)
112
+ y = solve_cubic_eq(b0, b1, b2)
113
+ a0 = np.zeros([len(a0), np.size(y, 1)]) + a0.reshape((len(a0), 1))
114
+ a1 = np.zeros([len(a1), np.size(y, 1)]) + a1.reshape((len(a1), 1))
115
+ a2 = np.zeros([len(a2), np.size(y, 1)]) + a2.reshape((len(a2), 1))
116
+ a3 = np.zeros([len(a3), np.size(y, 1)]) + a3.reshape((len(a3), 1))
117
+ R = np.emath.sqrt(a3 ** 2 / 4 - a2 + y)
118
+ S = np.zeros_like(R)
119
+ T = np.zeros_like(R)
120
+ i = R == 0
121
+ t1 = i.any().any()
122
+ if t1:
123
+ S[i] = 3 * a3[i] ** 2 / 4 - 2 * a2[i]
124
+ T[i] = 2 * np.emath.sqrt(y[i] ** 2 - 4 * a0[i])
125
+ else:
126
+ i = i == False
127
+ S[i] = 3 * a3[i] ** 2 / 4 - R[i] ** 2 - 2 * a2[i]
128
+ T[i] = (4 * np.multiply(a3[i], a2[i]) - 8 * a1[i] - a3[i] ** 3) / (4 * R[i])
129
+ # np.divide((4 * np.multiply(a3[i],a2[i]) - 8 * a1[i] - a3[i]**3),(4 * R[i]),T[i])
130
+ D = np.sqrt(S + T)
131
+ E = np.sqrt(S - T)
132
+ # s = np.array([-a3 / 2 + R + D, - a3 / 2 + R - D, - a3 / 2 - R + E, - a3 / 2 - R - E]) / 2
133
+ s = np.c_[-a3 / 2 + R + D, - a3 / 2 + R - D, - a3 / 2 - R + E, - a3 / 2 - R - E] / 2
134
+ return s
135
+
136
+
137
+ def sub2ind(sz, row, col):
138
+ 'matlab.sub2ind 函数'
139
+ n_rows = sz[0]
140
+ return [n_rows * (c - 1) + r for r, c in zip(row, col)]
141
+
142
+
143
+ def dis_ellipse(x, y, argE):
144
+ '点到椭圆的距离 Distance from point to ellipse'
145
+ # 准备工作
146
+ a = argE[2] # 长半轴 #Long semiaxis
147
+ b = argE[3] # 短半轴 #Short half axis
148
+ d = a ** 2 - b ** 2
149
+ # 将点转换为极坐标系 #Convert points to polar coordinate systems
150
+ x -= argE[0]
151
+ y -= argE[1]
152
+ q = np.dot(np.c_[x, y], np.array([[np.cos(argE[-1]), np.sin(argE[-1]) * -1], [np.sin(argE[-1]), np.cos(argE[-1])]]))
153
+ x = q[:, 0]
154
+ y = q[:, 1]
155
+ # 将点投影到第一象限当中 #Project points into the first quadrant
156
+ xq = np.abs(x)
157
+ yq = np.abs(y)
158
+ # 编辑方程参数 #Edit equation parameters
159
+ a3 = 2 * yq * b / d
160
+ a2 = (a ** 2 * xq ** 2 + b ** 2 * yq ** 2) / d ** 2 - 1
161
+ a1 = a3 * -1
162
+ a0 = -1 * b ** 2 * yq ** 2 / d ** 2
163
+ # 解四次方程。如果找不到有效的根,请将xp、yp和dist设置为Inf #Solve the quartic equation. If a valid root cannot be found, please set xp, yp, and dist to Inf
164
+ sinv = np.real(solve_quartic_eq(a0, a1, a2, a3))
165
+ cosv = np.sqrt(1 - sinv ** 2)
166
+ '''
167
+ i = sinv < 0
168
+ sinv[i] = np.inf
169
+ cosv[i] = np.inf
170
+ i = sinv > 1
171
+ sinv[i] = np.inf
172
+ cosv[i] = np.inf
173
+ '''
174
+ i = np.logical_or(sinv < 0, sinv > 1)
175
+ sinv[i] = np.inf
176
+ cosv[i] = np.inf
177
+ xp = a * cosv
178
+ yp = b * sinv
179
+ # 找到距离最小的根 #Find the root with the smallest distance
180
+ dist2 = (xq.reshape(-1, 1) - xp) ** 2 + (yq.reshape(-1, 1) - yp) ** 2
181
+ # where_are_nan = np.isnan(dist2)
182
+ # dist2[where_are_nan] = 1.1
183
+ j = np.argmin(dist2, axis=1)
184
+ # size = np.shape(dist2)
185
+ num = np.arange(len(dist2))
186
+ # ind = sub2ind(size, num, j)
187
+ sinv = sinv[num, j]
188
+ # Deal with the degrading cases
189
+ i = np.logical_and(yq == 0, xq >= d / a)
190
+ sinv[i] = 0
191
+ i = np.logical_and(yq == 0, xq < d / a)
192
+ sinv[i] = np.sqrt(1 - a ** 2 * xq[i] ** 2 / d ** 2)
193
+ # 查找第一象限中的正交接触点 #Find the positive contact point in the first quadrant
194
+ cosv = np.sqrt(1 - sinv ** 2)
195
+ xp = a * cosv
196
+ yp = b * sinv
197
+ dist = np.sqrt((xq - xp) ** 2 + (yq - yp) ** 2)
198
+ return dist
199
+
200
+ def fit_Ellipse_mp(x, z, c, points,num_cpu=mp.cpu_count(), dr=0.05):
201
+ '并行计算拟合椭圆 Parallel computing for fitting ellipses'
202
+ num_c = len(np.unique(c)) # 圆环数
203
+ xzlwp = np.empty([num_c, 5]) # 新建一个存储每个椭圆参数的容器
204
+ dis_all = np.zeros(len(points)) # 距离容器
205
+ # 并行计算加速准备 #Accelerated preparation for parallel computing
206
+ pool = mp.Pool(processes=num_cpu) # 开启多进程池,数量为cpu
207
+ tik = p0.cut_down(num_c) # 分块器
208
+ # tik2 = p0.cut_down(len(points)) # 分块器
209
+ tik_b = 0 # 分块输出计时器
210
+ # 并行计算每个圆环 Parallel computing for each torus
211
+ multi_res = [pool.apply_async(fit_Ellipse_block, args=(x, z, c, tik[i], tik[i + 1],points)) for i in
212
+ range(num_cpu)] # 将每个block需要处理的点云区间发送到每个进程当中
213
+ for res in multi_res:
214
+ # xzlwp[tik[tik_b]:tik[tik_b + 1], :] = res.get() # 将每个进程得到的结果分block的发送到容器当中
215
+ res_block = res.get()
216
+ dis_all += res_block[0]
217
+ xzlwp[tik[tik_b]:tik[tik_b + 1], :] = res_block[1]
218
+ tik_b += 1
219
+ pool.close() # 禁止进程池再接收任务 #Prohibit process pools from receiving tasks again
220
+ pool.join() # 当所有进程全部计算完毕后,退出并行计算 #After all processes have completed their calculations, exit parallel computing
221
+ # 下标
222
+ index = dis_all <= dr
223
+ outdex = dis_all > dr
224
+ # RMSE
225
+ dis_all = dis_all[dis_all<=dr]
226
+ RMSE = np.sqrt(np.sum(dis_all**2)/len(dis_all))
227
+ print('RMSE:', RMSE)
228
+ return index, outdex, xzlwp, dis_all
229
+
230
+ def fit_Ellipse_block(x, z, c, b, e,points):
231
+ '分块求椭圆参数 Calculating Ellipse Parameters by Blocks'
232
+ num_ = e-b
233
+ c_un = np.unique(c)
234
+ dis_ = np.zeros(len(points))
235
+ # dis_ = np.zeros(num_)
236
+ xzlwp = np.empty([num_, 5])
237
+ j = 0 # 循环计数器 cycle counter
238
+ for c1 in c_un[b:e]:
239
+ x_c1 = x[c == c1]
240
+ z_c1 = z[c == c1] # 提取单环的全部点云二维坐标 Extract all point cloud 2D coordinates of a single ring
241
+ data1 = np.vstack([x_c1, z_c1]).T
242
+ reg = LsqEllipse().fit(data1) # 输入二维点 Enter 2D points
243
+ center, width, height, phi = reg.as_parameters() # 求解 solve
244
+
245
+ xzlwp[j, 0] = center[0]
246
+ xzlwp[j, 1] = center[1]
247
+ xzlwp[j, 2] = np.max([width, height])
248
+ xzlwp[j, 3] = np.min([width, height])
249
+ xzlwp[j, 4] = phi
250
+
251
+ # xzlwp_ = np.array([center[0],center[1],np.max([width, height]),np.min([width, height]),phi])
252
+
253
+ # 求点到直线的距离 Find the distance from a point to a straight line
254
+ # dis_[points[:, -1]==c1] = dis_ellipse(points[points[:, -1]==c1, 0], points[points[:, -1]==c1, 2], xzlwp[j,:])
255
+ dis_[points[:, -1]==c1] = dis_ellipse(points[points[:, -1]==c1, 0], points[points[:, -1]==c1, 2], xzlwp[j,:])
256
+ j += 1
257
+ print('已完成第', b, '至第', e, '的圆环')
258
+ dis_ = np.nan_to_num(dis_, nan=1) # 若距离为nan,则转换为1
259
+ return dis_,xzlwp
260
+
261
+ class Ellipse:
262
+ def __init__(self, x, y, width, height, angle=0):
263
+ '''
264
+ :param x: 圆心x
265
+ :param y: 圆心y
266
+ :param width:长轴(不一定大于短轴)
267
+ :param height:短轴
268
+ :param angle:偏转角(弧度)
269
+ '''
270
+ self.x = x
271
+ self.y = y
272
+ self.width = width
273
+ self.height = height
274
+ self.angle = angle
275
+
276
+ def rotation_matrix(self):
277
+ """
278
+ Returns the rotation matrix for the ellipse's rotation.返回椭圆旋转的旋转矩阵。
279
+ """
280
+ a = math.cos(self.angle)
281
+ b = math.sin(self.angle)
282
+ return np.array([[a, -b], [b, a]])
283
+
284
+ def get_point(self, angle):
285
+ """
286
+ Returns the point on the ellipse at the specified local angle.以指定的局部角度返回椭圆上的点。
287
+ """
288
+ r = self.rotation_matrix()
289
+ xe = 0.5 * self.width * math.cos(angle)
290
+ ye = 0.5 * self.height * math.sin(angle)
291
+ return np.dot(r, [xe, ye]) + [self.x, self.y]
292
+
293
+ def get_points(self, count):
294
+ """
295
+ Returns an array of points around the ellipse in the specified count.返回指定计数的椭圆周围的点数组。
296
+ """
297
+ t = np.linspace(0, 2 * math.pi, count)
298
+ xe = 0.5 * self.width * np.cos(t)
299
+ ye = 0.5 * self.height * np.sin(t)
300
+ r = self.rotation_matrix()
301
+ return np.dot(np.column_stack([xe, ye]), r.T) + [self.x, self.y]
302
+
303
+ def find_distance1(self, x, tolerance=1e-8, max_iterations=10000, learning_rate=0.01):
304
+ """
305
+ Finds the minimum distance between the specified point and the ellipse
306
+ using gradient descent.查找指定点和椭圆之间的最小距离-使用梯度下降。
307
+ """
308
+ x = np.asarray(x)
309
+ r = self.rotation_matrix()
310
+ x2 = np.dot(r.T, x - [self.x, self.y])
311
+ t = math.atan2(x2[1], x2[0])
312
+ a = 0.5 * self.width
313
+ b = 0.5 * self.height
314
+ iterations = 0
315
+ error = tolerance
316
+ errors = []
317
+ ts = []
318
+
319
+ while error >= tolerance and iterations < max_iterations:
320
+ cost = math.cos(t)
321
+ sint = math.sin(t)
322
+ x1 = np.array([a * cost, b * sint])
323
+ xp = np.array([-a * sint, b * cost])
324
+ dp = 2 * np.dot(xp, x1 - x2)
325
+ t -= dp * learning_rate
326
+ error = abs(dp)
327
+ errors.append(error)
328
+ ts.append(t)
329
+ iterations += 1
330
+
331
+ ts = np.array(ts)
332
+ errors = np.array(errors)
333
+ y = np.linalg.norm(x1 - x2)
334
+ success = error < tolerance and iterations < max_iterations
335
+ return dict(x=t, y=y, error=error, iterations=iterations, success=success, xs=ts, errors=errors)
336
+
337
+ def find_distance2(self, x, tolerance=1e-8, max_iterations=1000):
338
+ """
339
+ Finds the minimum distance between the specified point and the ellipse
340
+ using Newton's method.查找指定点和椭圆之间的最小距离-使用牛顿法。
341
+ return:
342
+ x: 距离最近点所在的椭圆弧度值
343
+ y: 最短距离
344
+ """
345
+ x = np.asarray(x) # 输入值转换为numpy格式
346
+ r = self.rotation_matrix() # 旋转矩阵
347
+ x2 = np.dot(r.T, x - [self.x, self.y])
348
+ t = math.atan2(x2[1], x2[0])
349
+ a = 0.5 * self.width
350
+ b = 0.5 * self.height
351
+
352
+ # If point is inside ellipse, generate better initial angle based on vertices 如果点在椭圆内,则根据顶点生成更好的初始角度
353
+ if (x2[0] / a) ** 2 + (x2[1] / b) ** 2 < 1:
354
+ ts = np.linspace(0, 2 * math.pi, 24, endpoint=False)
355
+ xe = a * np.cos(ts)
356
+ ye = b * np.sin(ts)
357
+ delta = x2 - np.column_stack([xe, ye])
358
+ t = ts[np.argmin(np.linalg.norm(delta, axis=1))]
359
+
360
+ iterations = 0
361
+ error = tolerance
362
+ errors = []
363
+ ts = []
364
+
365
+ while error >= tolerance and iterations < max_iterations:
366
+ cost = math.cos(t)
367
+ sint = math.sin(t)
368
+ x1 = np.array([a * cost, b * sint])
369
+ xp = np.array([-a * sint, b * cost])
370
+ xpp = np.array([-a * cost, -b * sint])
371
+ delta = x1 - x2
372
+ dp = np.dot(xp, delta)
373
+ dpp = np.dot(xpp, delta) + np.dot(xp, xp)
374
+ t -= dp / dpp
375
+ error = abs(dp / dpp)
376
+ errors.append(error)
377
+ ts.append(t)
378
+ iterations += 1
379
+
380
+ ts = np.array(ts)
381
+ errors = np.array(errors)
382
+ y = np.linalg.norm(x1 - x2)
383
+ success = error < tolerance and iterations < max_iterations
384
+ return dict(x=t, y=y, error=error, iterations=iterations, success=success, xs=ts, errors=errors)
385
+
386
+ def find_dis_mp(x,z,l,s,t,points_xz,num_cpu = mp.cpu_count()):
387
+ '求点数组到椭圆的最近距离,在椭圆内的为﹣,在椭圆外的为+(基于并行计算)'
388
+ # 准备工作
389
+ num_xz = len(points_xz) # 点云数量
390
+ tik = p0.cut_down(num_xz) # 分块器
391
+ E = Ellipse(x,z,l*2,s*2,t) # 建立椭圆类
392
+ dis_all = np.empty(num_xz) # 距离容器
393
+ # j = 0 # 循环计数器
394
+ pool = mp.Pool(processes=num_cpu) # 开启多进程池,数量为cpu
395
+ # 开始并行计算
396
+ multi_res = [pool.apply_async(find_dis_block, args=(E, points_xz, tik[i], tik[i + 1])) for i in
397
+ range(num_cpu)] # 将每个block需要处理的点云区间发送到每个进程当中
398
+ tik_b = 0 # 分块输出计时器
399
+ for res in multi_res:
400
+ dis_all[tik[tik_b]:tik[tik_b + 1]] = res.get()
401
+ tik_b += 1
402
+ print('求距离进度',tik_b,'/',num_cpu)
403
+ pool.close() # 禁止进程池再接收任务 #Prohibit process pools from receiving tasks again
404
+ pool.join() # 当所有进程全部计算完毕后,退出并行计算 #After all processes have completed their calculations, exit parallel computing
405
+ print('开始添加正负号')
406
+ print(dis_all)
407
+ sign = E_sign(points_xz,[x,z],l,s,t) # 获取园内圆外判断
408
+ dis_all = np.multiply(dis_all,sign) # 融合距离和判断
409
+ return dis_all
410
+
411
+ def E_sign(points, Center, MA1, MA2,Theta):
412
+ '本函数只取得正负号'
413
+ x0, y0 = Center[0], Center[1]
414
+ a = MA1
415
+ b = MA2
416
+ phi = Theta
417
+ x, y = points[:, 0], points[:, 1]
418
+ cos_phi = np.cos(phi)
419
+ sin_phi = np.sin(phi)
420
+ X = x - x0
421
+ Y = y - y0
422
+ x_rot = cos_phi * X + sin_phi * Y
423
+ y_rot = -sin_phi * X + cos_phi * Y
424
+ distances = (x_rot / a) ** 2 + (y_rot / b) ** 2
425
+ # 获取正负号
426
+ distances -= 1
427
+ distances = np.sign(distances)
428
+ return distances
429
+
430
+ def find_dis_block(E,points_xz, b=0, e=0):
431
+ num_ = e - b # 分块的点云数量
432
+ dis_all_ = np.empty(num_) # 分块的距离容器
433
+ j = 0 # 计数器
434
+ for i in points_xz[b:e,:]:
435
+ dis_ = E.find_distance2(i) # 求单点
436
+ dis_all_[j] = dis_["y"] # 只要距离
437
+ j += 1 # 刷新计数器
438
+ return dis_all_
439
+
440
+ def Find_dis_second_Ellipse_mp(xzc, xzlst, num_ps, C_un):
441
+ '单线程求点到椭圆的距离(精拟合)'
442
+ dis_all = np.empty(num_ps) # 距离容器
443
+ tik = p0.cut_down(num_ps) # 分块器
444
+ xzlstc = np.c_[xzlst, C_un]
445
+ for i in range(len(tik)-1):
446
+ start, end = tik[i], tik[i + 1]
447
+ dis_all[start:end] = find_dis_second_Ellipse_block(xzc, xzlstc, start, end)
448
+ print('求距离进度', i, '/', num_ps)
449
+ return dis_all
450
+
451
+ def find_dis_second_Ellipse_block(xzc, xzlstc, b, e):
452
+ num_ = e - b # 分块的点云数量
453
+ dis_all_ = np.empty(num_) # 分块的距离容器
454
+ j = 0 # 计数器
455
+ for i in xzc[b:e, :]:
456
+ xzlst_ = xzlstc[xzlstc[:,-1]==i[-1],:-1]
457
+ # print(xzlst_)
458
+ E = Ellipse(xzlst_[0,0], xzlst_[0,1], xzlst_[0,2] * 2, xzlst_[0,3] * 2, xzlst_[0,4]) # 建立椭圆类
459
+ # print([i[0],i[1]])
460
+ dis_ = E.find_distance2(i[0:2]) # 求单点
461
+ dis_all_[j] = dis_["y"] # 只要距离
462
+ '添加正负号'
463
+ sign_ = E_sign_single(i[:2],[xzlst_[0,0], xzlst_[0,1]],xzlst_[0,2],xzlst_[0,3],xzlst_[0,4])
464
+ dis_all_[j] = np.multiply(dis_all_[j], sign_) # 融合距离和判断
465
+ j += 1 # 刷新计数器
466
+ return dis_all_
467
+
468
+ def E_sign_single(points, Center, MA1, MA2,Theta):
469
+ '本函数只取得单点正负号'
470
+ x0, y0 = Center[0], Center[1]
471
+ a = MA1
472
+ b = MA2
473
+ phi = Theta
474
+ x, y = points[0], points[1]
475
+ cos_phi = np.cos(phi)
476
+ sin_phi = np.sin(phi)
477
+ X = x - x0
478
+ Y = y - y0
479
+ x_rot = cos_phi * X + sin_phi * Y
480
+ y_rot = -sin_phi * X + cos_phi * Y
481
+ distances = (x_rot / a) ** 2 + (y_rot / b) ** 2
482
+ # 获取正负号
483
+ distances -= 1
484
+ distances = np.sign(distances)
485
+ return distances
486
+
487
+ def fit_ellipse_1(xyzic,xmax=3, xmin=-2.9, ymax=4.5, ymin=0.5):
488
+ '高精度拟合单个椭圆20240530'
489
+ # 1:限制坐标范围
490
+ xyzic_0 = xyzic[xyzic[:, 0] > xmin, :]
491
+ xyzic_0 = xyzic_0[xyzic_0[:, 0] < xmax, :]
492
+ xyzic_0 = xyzic_0[xyzic_0[:, 2] < ymax, :]
493
+ xyzic_0 = xyzic_0[xyzic_0[:, 2] > ymin, :]
494
+ # 2:第一次拟合
495
+ xz_0 = np.c_[xyzic_0[:, 0], xyzic_0[:, 2]] # 对拟合点进行降维
496
+ reg = LsqEllipse().fit(xz_0) # 输入二维点
497
+ center, width, height, phi = reg.as_parameters() # 求解
498
+ print(f'center: {center[0]:.3f}, {center[1]:.3f}')
499
+ print(f'width: {width:.3f}')
500
+ print(f'height: {height:.3f}')
501
+ print(f'phi: {phi:.3f}') # 打印
502
+ # 3:点到椭圆的距离
503
+ # arg_Ellipse = np.array([center[0], center[1], np.max([width, height]), np.min([width, height]), phi]) # 合并椭圆参数
504
+ # distances = ep.dis_ellipse(xyzic[:, 0], xyzic[:, 2], arg_Ellipse) # 求点到椭圆的距离
505
+ dis = find_dis_mp(center[0], center[1], width, height, phi,xyzic[:,[0,2]])
506
+ print('mean',np.mean(dis))
507
+ print('std',np.std(dis))
508
+ # plt.hist(dis, bins=100)
509
+ # plt.show()
510
+ # p1.view_pointclouds(xyzic[:,:3],dis,'dis','jet')
511
+ td = 0.000
512
+ dis_td = dis[dis>=td]
513
+ xyzic_1 = xyzic[dis>=td,:]
514
+ td = 0.05
515
+ xyzic_1 = xyzic_1[dis_td <= td, :]
516
+ dis_td = dis_td[dis_td <= td]
517
+ # p1.view_pointclouds(xyzic_1[:, :3], dis_td, 'dis', 'jet')
518
+ # 4.第二次拟合
519
+ xz_0 = np.c_[xyzic_1[:, 0], xyzic_1[:, 2]] # 对拟合点进行降维
520
+ reg = LsqEllipse().fit(xz_0) # 输入二维点
521
+ center, width, height, phi = reg.as_parameters() # 求解
522
+ print(f'center: {center[0]:.3f}, {center[1]:.3f}')
523
+ print(f'width: {width:.3f}')
524
+ print(f'height: {height:.3f}')
525
+ print(f'phi: {phi:.3f}') # 打印
526
+ dis = find_dis_mp(center[0], center[1], width, height, phi,xyzic[:,[0,2]])
527
+ dis_min = find_dis_mp(center[0], center[1], width, height, phi,xyzic_1[:,[0,2]])
528
+ print('mean',np.mean(dis_min))
529
+ print('std',np.std(dis_min))
530
+ td = np.mean(dis_min)+1.4*np.std(dis_min)
531
+ xyzic_f = xyzic[np.abs(dis) <= td,:]
532
+ dis = dis[np.abs(dis) <= td]
533
+ # p1.view_pointclouds(xyzic_f[:, :3], dis, 'dis', 'jet')
534
+ # 5.最终参数
535
+ xz_0 = np.c_[xyzic_f[:, 0], xyzic_f[:, 2]] # 对拟合点进行降维
536
+ reg = LsqEllipse().fit(xz_0) # 输入二维点
537
+ center, width, height, phi = reg.as_parameters() # 求解
538
+ print(f'center: {center[0]:.3f}, {center[1]:.3f}')
539
+ print(f'width: {width:.3f}')
540
+ print(f'height: {height:.3f}')
541
+ print(f'phi: {phi:.3f}') # 打印
542
+ return np.array([center[0], center[1], width, height, phi]),xyzic_f # 返回椭圆参数和在椭圆上的点
543
+
544
+
545
+ if __name__ == '__main__':
546
+ '椭圆拟合例子 Ellipse fitting example'
547
+ # arg_Ellipse = fitellipse(xy) # size(xy) = [n,2]
548
+ '点到椭圆例子Example of point to ellipse'
549
+ eps = np.array([0, 0, 1, .5, np.pi / 6]) # Ellipse parameter
550
+ # Plane point coordinates
551
+ x = np.array([0.0,10.2,7.98760,1.198760,1.198730,0.198760,-4.999760])
552
+ y = np.array([0.0,7.198730,8.198760,5.198760,7.198730,8.198760,-4.779760])
553
+ dist = dis_ellipse(x, y, eps) # Finding the Distance from a Point to an Ellipse
554
+ print(dist)