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 +16 -0
- zelas2-1.0.1/README.md +1 -0
- zelas2-1.0.1/Zelas2.egg-info/PKG-INFO +16 -0
- zelas2-1.0.1/Zelas2.egg-info/SOURCES.txt +16 -0
- zelas2-1.0.1/Zelas2.egg-info/dependency_links.txt +1 -0
- zelas2-1.0.1/Zelas2.egg-info/top_level.txt +1 -0
- zelas2-1.0.1/setup.cfg +4 -0
- zelas2-1.0.1/setup.py +30 -0
- zelas2-1.0.1/zelas2/Ellipse.py +554 -0
- zelas2-1.0.1/zelas2/Multispectral.py +406 -0
- zelas2-1.0.1/zelas2/RedundancyElimination.py +939 -0
- zelas2-1.0.1/zelas2/TheHeartOfTheMilitaryGod.py +364 -0
- zelas2-1.0.1/zelas2/__init__.py +9 -0
- zelas2-1.0.1/zelas2/dxf_dwg.py +52 -0
- zelas2-1.0.1/zelas2/ransac.py +243 -0
- zelas2-1.0.1/zelas2/setup.py +37 -0
- zelas2-1.0.1/zelas2/shield.py +2284 -0
- zelas2-1.0.1/zelas2/voxel.py +1526 -0
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
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
zelas2
|
zelas2-1.0.1/setup.cfg
ADDED
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)
|