我有一系列3D积分。这些点以恒定水平(z = 0,1,...,7)进行采样。图像应该清楚:
这些点位于形状(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])
,但这导致了以下混乱:
任何帮助?
以下是生成代表问题的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:
以下是沿着常数z的切片:
答案 0 :(得分:11)
我现在这样做:
matplotlib.path
来确定轮廓内外的点。使用这个想法,我将每个切片中的轮廓转换为布尔值图像,该图像被组合成一个布尔值的体积。skimage
's marching_cubes
方法获取表面的三角剖分以进行可视化。这是该方法的一个例子。我认为数据略有不同,但你可以肯定地看到结果更清晰,并且可以处理断开连接或有孔的表面。
我应该为将来遇到它的人更新这个。虽然上述方法大部分时间都有效,但它确实(通过球面坐标变换)假设没有两个点位于同一条光线上。如果您注意到上图中左侧的工件,这就是原因。
更好的方法是做手术"在表面上。考虑到像橘皮一样的表面,你将它切成一边,然后将它张开,拉伸它。然后你有一个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中的样本数据,得到:
答案 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()
在这种情况下,它确实很好,因为你的示例数据最终位于一个或多或少的凸面上。也许您可以制作更具挑战性的示例数据?环形表面将是一个很好的测试案例,凸壳方法显然会失败。
从点云映射任意3D表面是真正难题。这是一个related question,其中包含一些可能有用的链接。