基于3D数据集拟合椭圆曲面并计算面积的Python实现方案
3D类椭圆点集的椭圆拟合与面积计算方案
方法概述
由于你的点集近似分布在一个平面上(类椭圆火焰边缘),最优方案是:
- 求解点集的最佳拟合平面
- 将3D点投影到该平面得到2D坐标
- 对2D点拟合椭圆并计算面积
这种方法相比三角拟合,能生成光滑的椭圆形状,避免面积高估和杂乱曲面问题。
实现代码
import numpy as np from skimage.measure import EllipseModel import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # 加载给定数据 data = np.array([[ 0.14893939, 0.14893939, 0.14621212, 0.14621212, 0.14651515, 0.14651515, 0.14984848, 0.14984848, 0.15015152, 0.15015152, 0.15045455, 0.15045455, 0.15075758, 0.15075758, 0.14439394, 0.14439394, 0.1519697, 0.1519697, 0.14378788, 0.14378788, 0.14348485, 0.14348485, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15318182, 0.15287879, 0.15287879, 0.15287879, 0.15287879, 0.14378788, 0.14378788, 0.14378788, 0.14378788, 0.14409091, 0.14409091, 0.14409091, 0.14409091, 0.1519697, 0.1519697, 0.15166667, 0.15166667, 0.15106061, 0.15106061, 0.15136364, 0.15136364, 0.14469697, 0.14469697, 0.15015152, 0.15015152, 0.15045455, 0.15045455, 0.14530303, 0.14530303, 0.14530303, 0.14560606, 0.14560606, 0.14560606, 0.1480303, 0.1480303], [ 0.14560606, 0.14560606, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14590909, 0.14651515, 0.14651515, 0.14681818, 0.14681818, 0.1480303, 0.1480303, 0.14924242, 0.14924242, 0.14924242, 0.14924242, 0.15045455, 0.15045455, 0.15075758, 0.15075758, 0.15106061, 0.15106061, 0.15136364, 0.15136364, 0.15166667, 0.15166667, 0.1519697, 0.1519697, 0.15287879, 0.15287879, 0.15318182, 0.15318182, 0.15409091, 0.15409091, 0.15439394, 0.15439394, 0.15469697, 0.15469697, 0.155, 0.155, 0.155, 0.155, 0.15530303, 0.15530303, 0.15560606, 0.15560606, 0.15560606, 0.15560606, 0.15621212, 0.15621212, 0.15621212, 0.15621212, 0.15621212, 0.15621212, 0.15651515, 0.15651515, 0.15651515, 0.15651515, 0.15651515, 0.15651515, 0.15651515, 0.15651515], [ 0.01907071, 0.01921212, 0.01935354, 0.01949495, 0.01935354, 0.01949495, 0.01907071, 0.01921212, 0.01907071, 0.01921212, 0.01907071, 0.01921212, 0.01907071, 0.01921212, 0.02062626, 0.02076768, 0.01878788, 0.01892929, 0.02034343, 0.02048485, 0.02006061, 0.02020202, 0.01793939, 0.01808081, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01765657, 0.01779798, 0.01935354, 0.01949495, 0.01935354, 0.01949495, 0.01935354, 0.01949495, 0.01935354, 0.01949495, 0.01793939, 0.01808081, 0.01793939, 0.01808081, 0.01793939, 0.01808081, 0.01793939, 0.01808081, 0.02006061, 0.02020202, 0.01822222, 0.01836364, 0.01822222, 0.01836364, 0.01963636, 0.01977778, 0.01991919, 0.01963636, 0.01977778, 0.01991919, 0.01822222, 0.01836364]]).T # 步骤1:计算最佳拟合平面 # 中心化数据 mean = np.mean(data, axis=0) centered_data = data - mean # SVD分解求平面法向量 u, s, vh = np.linalg.svd(centered_data) normal = vh[-1] # 最小奇异值对应的向量是平面法向量 # 平面方程:normal · (x - mean) = 0 → ax + by + cz + d = 0 d = -np.dot(normal, mean) # 步骤2:将3D点投影到平面,转换为2D坐标 # 构建平面内的正交坐标系 # 取第一个主方向作为u轴 u_axis = vh[0] # v轴为法向量与u轴的叉乘,确保正交 v_axis = np.cross(normal, u_axis) # 归一化轴向量 u_axis /= np.linalg.norm(u_axis) v_axis /= np.linalg.norm(v_axis) # 投影到2D坐标 projected_2d = np.array([ [np.dot(p - mean, u_axis), np.dot(p - mean, v_axis)] for p in data ]) # 步骤3:拟合2D椭圆 ellipse_model = EllipseModel() ellipse_model.fit(projected_2d) # 获取椭圆参数:(中心x, 中心y, 半长轴a, 半短轴b, 旋转角theta) x0, y0, a, b, theta = ellipse_model.params # 步骤4:计算椭圆面积 ellipse_area = np.pi * a * b print(f"拟合椭圆的面积:{ellipse_area:.6f}") # 可选:可视化结果 fig = plt.figure(figsize=(12, 6)) # 3D视图:原始点与拟合平面 ax1 = fig.add_subplot(121, projection='3d') ax1.scatter(data[:,0], data[:,1], data[:,2], c='r', label='原始点') # 绘制拟合平面 xx, yy = np.meshgrid(np.linspace(data[:,0].min(), data[:,0].max(), 10), np.linspace(data[:,1].min(), data[:,1].max(), 10)) zz = (-normal[0]*xx - normal[1]*yy - d) / normal[2] ax1.plot_surface(xx, yy, zz, alpha=0.3, label='拟合平面') ax1.set_xlabel('X') ax1.set_ylabel('Y') ax1.set_zlabel('Z') ax1.legend() ax1.set_title('3D点集与拟合平面') # 2D视图:投影点与拟合椭圆 ax2 = fig.add_subplot(122) ax2.scatter(projected_2d[:,0], projected_2d[:,1], c='r', label='投影点') # 生成椭圆上的点用于绘制 theta_vals = np.linspace(0, 2*np.pi, 100) ellipse_x = x0 + a*np.cos(theta_vals)*np.cos(theta) - b*np.sin(theta_vals)*np.sin(theta) ellipse_y = y0 + a*np.cos(theta_vals)*np.sin(theta) + b*np.sin(theta_vals)*np.cos(theta) ax2.plot(ellipse_x, ellipse_y, 'b-', label='拟合椭圆') ax2.set_xlabel('U轴') ax2.set_ylabel('V轴') ax2.legend() ax2.set_title('2D投影点与拟合椭圆') ax2.axis('equal') plt.tight_layout() plt.show()
关键说明
- 平面拟合:通过SVD分解找到点集的最小方差方向(法向量),确保平面最佳贴合所有点。
- 2D投影:在平面内构建正交坐标系,将3D点无损转换为2D坐标,保留原始形状关系。
- 椭圆拟合:使用
skimage.measure.EllipseModel确保拟合结果为椭圆(而非其他二次曲线),参数直接给出半轴长度。 - 面积计算:椭圆面积公式为
π*a*b,其中a和b是拟合得到的半长轴和半短轴长度,结果准确反映类椭圆火焰边缘的实际面积。
内容的提问来源于stack exchange,提问作者balcieb
相关产品推荐
相关产品推荐

