返回3D scipy.spatial.Delaunay的表面三角形

13 投票
3 回答
29414 浏览
提问于 2025-04-28 05:12

我遇到了一个问题。我想用scipy.spatial.Delaunay来处理点云,进行三角剖分。我用了以下代码:

tri = Delaunay(points) # points: np.array() of 3d points 
indices = tri.simplices
vertices = points[indices]

但是,这段代码返回的是四面体。怎么才能只得到表面的三角形呢?

谢谢

暂无标签

3 个回答

3

根据Jaime的回答,我想再详细解释一下,并给个例子:

import matplotlib as mpl
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d as a3
import numpy as np
import scipy as sp
from scipy import spatial as sp_spatial


def icosahedron():
    h = 0.5*(1+np.sqrt(5))
    p1 = np.array([[0, 1, h], [0, 1, -h], [0, -1, h], [0, -1, -h]])
    p2 = p1[:, [1, 2, 0]]
    p3 = p1[:, [2, 0, 1]]
    return np.vstack((p1, p2, p3))


def cube():
    points = np.array([
        [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1],
        [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1],
    ])
    return points


points = icosahedron()
# points = cube()

hull = sp_spatial.ConvexHull(points)
indices = hull.simplices
faces = points[indices]

print('area: ', hull.area)
print('volume: ', hull.volume)


fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.dist = 30
ax.azim = -140
ax.set_xlim([0, 2])
ax.set_ylim([0, 2])
ax.set_zlim([0, 2])
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')

for f in faces:
    face = a3.art3d.Poly3DCollection([f])
    face.set_color(mpl.colors.rgb2hex(sp.rand(3)))
    face.set_edgecolor('k')
    face.set_alpha(0.5)
    ax.add_collection3d(face)

plt.show()

这个例子应该能展示出下面这个图形:

这里输入图片描述

4

看起来你想要计算你的点云的凸包。我觉得这就是你想要做的事情:

from scipy.spatial import ConvexHull

hull = ConvexHull(points)
indices = hull.simplices
vertices = points[indices]
11

要让它以代码的形式正常工作,你需要把表面参数化为二维的。比如在球体的情况下(r, theta, psi),半径是固定的(可以忽略),而点的位置由(theta, psi)来表示,这样就是二维的了。

Scipy Delaunay 是一种N维的三角剖分,所以如果你给它三维的点,它就会返回三维的对象。如果你给它二维的点,它就会返回二维的对象。

下面是我用来为openSCAD创建多面体的脚本。U和V是我的参数化(相当于x和y),这些就是我给Delaunay的坐标。注意,现在“Delaunay三角剖分的属性”只适用于u,v坐标(在uv空间中角度被最大化,而不是在xyz空间中,等等)。

这个例子是从http://matplotlib.org/1.3.1/mpl_toolkits/mplot3d/tutorial.html修改过来的,原本使用的是Triangulation函数(最终映射到Delaunay?)

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.tri as mtri
from scipy.spatial import Delaunay

# u, v are parameterisation variables
u = np.array([0,0,0.5,1,1]) 
v = np.array([0,1,0.5,0,1]) 

x = u
y = v
z = np.array([0,0,1,0,0])

# Triangulate parameter space to determine the triangles
#tri = mtri.Triangulation(u, v)
tri = Delaunay(np.array([u,v]).T)

print 'polyhedron(faces = ['
#for vert in tri.triangles:
for vert in tri.simplices:
    print '[%d,%d,%d],' % (vert[0],vert[1],vert[2]),
print '], points = ['
for i in range(x.shape[0]):
    print '[%f,%f,%f],' % (x[i], y[i], z[i]),
print ']);'


fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')

# The triangles in parameter space determine which x, y, z points are
# connected by an edge
#ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap=plt.cm.Spectral)
ax.plot_trisurf(x, y, z, triangles=tri.simplices, cmap=plt.cm.Spectral)


plt.show()

上面例子绘制的金字塔

下面是(稍微结构化一些的)文本输出:

polyhedron(
    faces = [[2,1,0], [3,2,0], [4,2,3], [2,4,1], ], 

    points = [[0.000000,0.000000,0.000000], 
              [0.000000,1.000000,0.000000], 
              [0.500000,0.500000,1.000000], 
              [1.000000,0.000000,0.000000], 
              [1.000000,1.000000,0.000000], ]);

撰写回答