Python 将曲线样条曲线拟合到三维点云 客观的

Python 将曲线样条曲线拟合到三维点云 客观的,python,geometry,spline,Python,Geometry,Spline,我有一个3D刻面模型(例如,关闭文件),例如,它看起来像管道(参见示例图片)。目标是使用python导出近似样条线(线和样条线的最佳组合),该样条线表示此管的三维骨架 目前技术水平 同一字段中的Stackoverflow posts: 概述: PowerCastle/NN外壳?我读过这些,但是我找不到python实现,也许我太笨了,不能自己实现它。据我所知,基础是delaunay/voronoi,我已经知道了。然而,我不知道还有什么进一步的步骤 我的方法(到目前为止) 从示例fac

我有一个3D刻面模型(例如,关闭文件),例如,它看起来像管道(参见示例图片)。目标是使用python导出近似样条线(线和样条线的最佳组合),该样条线表示此管的三维骨架

目前技术水平 同一字段中的Stackoverflow posts:

概述:

  • PowerCastle/NN外壳?我读过这些,但是我找不到python实现,也许我太笨了,不能自己实现它。据我所知,基础是delaunay/voronoi,我已经知道了。然而,我不知道还有什么进一步的步骤
我的方法(到目前为止) 从示例facet模型(图1)开始,我使用python包将3d模型转换为点云(图2)。该点云可用于体素化表示(图3)。因此,这三类数据是我的出发点

基本上,这个问题对我来说并不太复杂,但是我缺少一个开始的逻辑。大多数研究论文将这一点过度复杂化,用于各种深入的任务。 一个想法是做一个主成分分析来推导组件的主轴,并沿着这些轴进行扫描。然而,这似乎并没有带来良好的绩效结果。 另一个想法是使用体素化网格并检测由于体素邻接而产生的路径。 另一个想法是使用KD树计算最近点,以检测通过其平面法线定义样条线方向的正确平面

我尝试的一种方法是从点云中选择N个随机点,并搜索半径内的所有邻居(cKDTree.query\u ball\u point)。我计算了所有相邻点的中心。这导致了图4中的结果。结果似乎与第一种方法一样好,但它或多或少是对半径参数的调整

图1:

图2:

图3:

图4:
由于中轴是Voronoi图的子图,因此Delaunay/Voronoi方法可用于此问题 (例如,见Attali、Boissonnat和Edelsbrunner的著作)

在下文中,我将演示从半径为10、半径为100的四分之一圆环曲面(中间路径/骨架从点(100,0,0)开始,到点(0,100,0)结束)采样点的示例方法

Voronoi图是3D Delaunay四面体化的对偶(从现在起,我将使用术语三角剖分)。 可以使用scipy的
scipy.spatial.Delaunay
包计算Delaunay三角剖分

下图是采样点(本例中为200个)及其完整的Delaunay三角剖分 (使用函数绘制三角剖分)。

与Delaunay四面体相对应的Voronoi顶点是四面体的外接球的中心。 下面是一个计算这些Delaunay中心的函数,它是我先前答案中2D函数的扩展

我们希望过滤掉原始曲面之外的四面体(例如上图中的长四面体)。 这可以通过在原始表面上测试四面体来实现。 但是,有一种更简单的方法非常适合输入三维布管曲面 过滤出具有较大外切半径的四面体。 这就是算法所做的。 这在我们的上下文中很容易做到,因为半径就是中心和任何四面体点之间的距离

下图显示了过滤掉半径大于20的四面体后的Delaunay三角剖分

我们现在可以使用这些构建块来构建通过半径条件的四面体的Voronoi子图。 下面的函数使用Delaunay三角剖分中的连通性信息来构造Voronoi子图,表示为边列表

def compute_voronoi_vertices_and_edges(points, r_thresh=np.inf):
"""
Compute (finite) Voronoi edges and vertices of a set of points.
:param points: input points.
:param r_thresh: radius value for filtering out vertices corresponding to
Delaunay tetrahedrons with large radii of circumscribing sphere (alpha-shape condition).
:return: array of xyz Voronoi vertex points and an edge list.
"""
dt = Delaunay(points)
xyz_centers = compute_delaunay_tetra_circumcenters(dt)

# filtering tetrahedrons that have radius > thresh
simp_pts_0 = dt.points[dt.simplices[:, 0]]
radii = np.linalg.norm(xyz_centers - simp_pts_0, axis=1)
is_in = radii < r_thresh

# build an edge list from (filtered) tetrahedrons neighbor relations
edge_lst = []
for i in range(len(dt.neighbors)):
    if not is_in[i]:
        continue  # i is an outside tetra
    for j in dt.neighbors[i]:
        if j != -1 and is_in[j]:
            edge_lst.append((i, j))

return xyz_centers, edge_lst
下图显示了原始200点的结果

这是1000个点的密集样本的结果

现在可以通过路径点传递近似样条插值或最小二乘拟合。 您可以使用链接中建议的
scipy.interpolate.UnivariateSpline

scipy.interpolate.splrep
完成或任何其他标准样条线实现。

曲面模型中的面是否结构化(例如,所有四边形或三角形四边形,其中一条边沿管方向)?我不这么认为,或者至少我不想认为这是理所当然的,因为它描述了另一个边界条件。现在我将在问题中添加我的另一次尝试谢谢@Iddo Hanniel为您所做的巨大努力!这似乎真的很有希望。唯一的问题是必须调整距离参数,但这似乎是一个很好的解决方案。不幸的是,我现在没有太多的空闲时间,我会尽快测试它!你好我不确定如何处理路径的对象。nx.shortest_path返回一个整数列表,该列表的长度大于原始输入数据的长度。这些整数是什么意思?如何得到最后两个图中显示的黑线的坐标?非常感谢。path_对象是xyz_中心数组中的索引列表。因此[xyz_centers[i,:]for i in path_s]应该为您提供黑线的(有序)3d坐标列表。
def compute_voronoi_vertices_and_edges(points, r_thresh=np.inf):
"""
Compute (finite) Voronoi edges and vertices of a set of points.
:param points: input points.
:param r_thresh: radius value for filtering out vertices corresponding to
Delaunay tetrahedrons with large radii of circumscribing sphere (alpha-shape condition).
:return: array of xyz Voronoi vertex points and an edge list.
"""
dt = Delaunay(points)
xyz_centers = compute_delaunay_tetra_circumcenters(dt)

# filtering tetrahedrons that have radius > thresh
simp_pts_0 = dt.points[dt.simplices[:, 0]]
radii = np.linalg.norm(xyz_centers - simp_pts_0, axis=1)
is_in = radii < r_thresh

# build an edge list from (filtered) tetrahedrons neighbor relations
edge_lst = []
for i in range(len(dt.neighbors)):
    if not is_in[i]:
        continue  # i is an outside tetra
    for j in dt.neighbors[i]:
        if j != -1 and is_in[j]:
            edge_lst.append((i, j))

return xyz_centers, edge_lst
# get closest vertex to start and end points
xyz_centers, edge_lst = compute_voronoi_vertices_and_edges(pts, r_thresh=20.)
kdt = cKDTree(xyz_centers)
dist0, idx0 = kdt.query(np.array([100., 0, 0]))
dist1, idx1 = kdt.query(np.array([0, 100., 0]))

# compute shortest weighted path
edge_lengths = [np.linalg.norm(xyz_centers[e[0], :] - xyz_centers[e[1], :]) for e in edge_lst]
g = nx.Graph((i, j, {'weight': dist}) for (i, j), dist in zip(edge_lst, edge_lengths))
path_s = nx.shortest_path(g,source=idx0,target=idx1, weight='weight')