Delaunay使用python从3D表面对2D点进行三角测量?

时间:2015-04-22 14:28:51

标签: python numpy matplotlib data-visualization

我有一系列3D积分。这些点以恒定水平(z = 0,1,...,7)进行采样。图像应该清楚:

point collection

这些点位于形状(N, 3)的numpy ndarray中,称为X。上面的图使用:

创建
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

X = load('points.npy')
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.plot_wireframe(X[:,0], X[:,1], X[:,2])
ax.scatter(X[:,0], X[:,1], X[:,2])
plt.draw()

我想仅对该物体的表面进行三角测量,并绘制曲面。但是,我不想要这个物体的凸包,因为这会丢失我希望能够检查的细微形状信息。

我尝试了ax.plot_trisurf(X[:,0], X[:,1], X[:,2]),但这导致了以下混乱:

mess

任何帮助?

示例数据

以下是生成代表问题的3D数据的代码段:

import numpy as np
X = []
for i in range(8):
    t = np.linspace(0,2*np.pi,np.random.randint(30,50))
    for j in range(t.shape[0]):
        # random circular objects...
        X.append([
            (-0.05*(i-3.5)**2+1)*np.cos(t[j])+0.1*np.random.rand()-0.05,
            (-0.05*(i-3.5)**2+1)*np.sin(t[j])+0.1*np.random.rand()-0.05,
            i
        ])
X = np.array(X)

原始图像的示例数据

这是原始数据的pastebin:

http://pastebin.com/YBZhJcsV

以下是沿着常数z的切片:

enter no image description here

2 个答案:

答案 0 :(得分:11)

更新2

我现在这样做:

  1. 我使用的事实是每个z切片中的路径都是闭合且简单的,并使用matplotlib.path来确定轮廓内外的点。使用这个想法,我将每个切片中的轮廓转换为布尔值图像,该图像被组合成一个布尔值的体积。
  2. 接下来,我使用skimage's marching_cubes方法获取表面的三角剖分以进行可视化。
  3. 这是该方法的一个例子。我认为数据略有不同,但你可以肯定地看到结果更清晰,并且可以处理断开连接或有孔的表面。

    更新1(仍然不好)

    我应该为将来遇到它的人更新这个。虽然上述方法大部分时间都有效,但它确实(通过球面坐标变换)假设没有两个点位于同一条光线上。如果您注意到上图中左侧的工件,这就是原因。

    更好的方法是做手术"在表面上。考虑到像橘皮一样的表面,你将它切成一边,然后将它张开,拉伸它。然后你有一个2D平面,你可以进行三角测量和插值。您只需跟踪如何返回3D中的相应位置。实现这个想法需要相当多的工作,并且实现还需要特别注意我的数据的表示方式。

    无论如何,这只是为了说明人们如何更有力地接近这一点。

    原始答案

    好的,这是我提出的解决方案。它在很大程度上取决于我的数据大致为球形并且在z中均匀采样。其他一些评论提供了有关更强大解决方案的更多信息。由于我的数据是大致球形,我从数据点的球面坐标变换三角测量方位角和天顶角。

    import numpy as np
    import matplotlib.pyplot as plt 
    from mpl_toolkits.mplot3d import Axes3D
    import matplotlib.tri as mtri
    
    X = np.load('./mydatars.npy')
    # My data points are strictly positive. This doesn't work if I don't center about the origin.
    X -= X.mean(axis=0)
    
    rad = np.linalg.norm(X, axis=1)
    zen = np.arccos(X[:,-1] / rad)
    azi = np.arctan2(X[:,1], X[:,0])
    
    tris = mtri.Triangulation(zen, azi)
    
    fig = plt.figure()
    ax  = fig.add_subplot(111, projection='3d')
    ax.plot_trisurf(X[:,0], X[:,1], X[:,2], triangles=tris.triangles, cmap=plt.cm.bone)
    plt.show()
    

    使用上面的pastebin中的样本数据,得到:

    no thanks

答案 1 :(得分:9)

我意识到你在问题中提到你并不想使用凸包,因为你可能会丢失一些形状信息。我有一个简单的解决方案,适用于你的“抖动球形”#39;示例数据,尽管它确实使用scipy.spatial.ConvexHull。无论如何,我想我会在这里分享它,以防它对其他人有用:

from matplotlib.tri import triangulation
from scipy.spatial import ConvexHull

# compute the convex hull of the points
cvx = ConvexHull(X)

x, y, z = X.T

# cvx.simplices contains an (nfacets, 3) array specifying the indices of
# the vertices for each simplical facet
tri = Triangulation(x, y, triangles=cvx.simplices)

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.hold(True)
ax.plot_trisurf(tri, z)
ax.plot_wireframe(x, y, z, color='r')
ax.scatter(x, y, z, color='r')

plt.draw()

enter image description here

在这种情况下,它确实很好,因为你的示例数据最终位于一个或多或少的凸面上。也许您可以制作更具挑战性的示例数据?环形表面将是一个很好的测试案例,凸壳方法显然会失败。

从点云映射任意3D表面是真正难题。这是一个related question,其中包含一些可能有用的链接。