编辑:我已经在这里写了一个更简明的问题版本,但我仍然保留这篇文章,因为它是一个完整的解释。
给定一个3D NumPy数组,marching cubes可以在某个阈值周围形成一个3D 对象。
import numpy as np
from skimage import measure
A = np.zeros((12,12,12))
#A[A<1] = -1
for i in np.arange(1,2):
for j in np.arange(1,2):
for k in np.arange(1,2):
A[i,j,k] = 10
for i in np.arange(8,9):
for j in np.arange(8,9):
for k in np.arange(8,9):
A[i,j,k] = 10
verts, faces, normals, values = measure.marching_cubes_lewiner(A,1)
# which returns
verts = [[0.1, 1., 1. ] [1., 1., 0.1] [1., 0.1, 1. ] [1., 1., 1.9] [1., 1.9, 1. ]
[1.9, 1., 1. ] [7.1, 8., 8. ] [8., 8., 7.1] [8., 7.1, 8. ] [8., 8., 8.9]
[8., 8.9, 8. ] [8.9, 8., 8. ]]
faces = [[ 2, 1, 0] [ 0, 3, 2] [ 1, 4, 0] [ 0, 4, 3] [ 5, 1, 2] [ 3, 5, 2]
[ 5, 4, 1] [ 4, 5, 3] [ 8, 7, 6] [ 6, 9, 8] [ 7, 10, 6] [ 6, 10, 9]
[11, 7, 8] [ 9, 11, 8] [11, 10, 7] [10, 11, 9]]
这可以被绘制出来:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from mpl_toolkits.mplot3d import Axes3D
mesh = Poly3DCollection(verts[faces])
mesh.set_edgecolor('k')
mesh.set_facecolor('b')
ax.set_xlim(0,10)
ax.set_ylim(0,10)
ax.set_zlim(0,12)
返回这个漂亮的 3D 图像:
我使用自己的代码(见下文)来分离这些物体,得到:
graph1 = {(1.0, 1.0, 0.10000000149011612), (1.899999976158142, 1.0, 1.0), (0.10000000149011612, 1.0, 1.0), (1.0, 1.899999976158142, 1.0), (1.0, 0.10000000149011612, 1.0), (1.0, 1.0, 1.899999976158142)}
graph2 = {(8.899999618530273, 8.0, 8.0), (8.0, 8.899999618530273, 8.0), (7.099999904632568, 8.0, 8.0), (8.0, 8.0, 7.099999904632568), (8.0, 7.099999904632568, 8.0), (8.0, 8.0, 8.899999618530273)}
现在的问题是,虽然我已经找到了组成每个图形的顶点,但我不再有一种简单的方法来为每个对象创建单独的三维网格。之前,使用 verts[faces]
来创建网格,但如何将每个 graph
与 faces
相关联以创建三角形网格并不明显。我已经尝试解决这个问题,但没有成功。例如:
verts1 = verts[0:6]
faces1 = faces[0:6]
mesh = Poly3DCollection(verts1[faces1])
这不起作用。我认为关键是找到与每个对象对应的面。如果这样做,它可能会起作用。例如,我们的第一个图仅包括顶点1至6。因此,我们只需要引用那些顶点的faces
。作为演示,可以使用以下代码重新创建第一个图graph1
(而不使用graph2
):
faces1 = faces[0:8]
mesh = Poly3DCollection(verts[faces1])
# and plot like above
如果我可以记录不仅顶点本身,还包括它们的索引,那么我就可以对引用该对象的那些faces
进行排序。我来解释一下。首先遇到的问题是,我没有这些索引。这是我的对象排序方式。我们首先创建一个线列表(或边列表),然后将它们变成元组,并使用networkx查找连接部件。
# create linelist
linelist = []
for idx, vert in enumerate(faces):
for i,x in enumerate(vert):
l = [np.ndarray.tolist(verts[faces[idx][i]]), np.ndarray.tolist(verts[faces[idx][(i+1)%len(vert)]])] # connect the verts of the triangle
linelist.append(l) # add to the line list
# Creates graph
tmp = [tuple(tuple(j) for j in i) for i in linelist]
graph = nx.Graph(tmp)
graphs = []
i=0
for idx, graph in enumerate(sorted(nx.connected_components(graph),key = len, reverse = True)):
graphs.append((graph))
print("Graph ",idx," corresponds to vertices: ",graph,'\n\n',file=open("output.txt","a"))
i+=1
我不知道networkx如何记录每个顶点的索引。
其次,可能会出现每个对象引用的faces
是不相交的情况,即它可能是faces[0:4] + faces[66] + faces[100:110]
。然而,这可能是可以克服的。
假设我们可以为每个图形生成一个索引列表,主要问题是发现一种有效的方法来发现哪些面引用了那些顶点。我的解决方案适用于这组对象,但对于更复杂的排列(我可以提供),它也非常缓慢。尽管如此,这里是:
objects = []
obj = []
i = 0
for idx, face in enumerate(M):
if i == 0:
obj.append(face)
i = i + 1
else:
if np.isin(face,obj).any():
obj.append(face)
else:
objects.append(obj.copy())
obj = []
obj.append(face)
i = 0
if idx == len(M)-1:
objects.append(obj.copy())
如果你读到这里,我对社区的印象深刻。我认为也许有一种有效的方法可以用 networkx 实现,但我还没有找到它。
期望输出: 我想将面孔按连接组件排序,就像我对顶点排序一样。 graph1 = faces[x1] + faces[x2] + ... + faces[xn]
。
编辑:如果有人能帮我编码,我有一个想法(部分感谢 @Ehsan)。在分成连接组件并找到图形后,每个组件的顶点可以被哈希以查找原始索引。然后,您可能能够搜索包括至少一个这些索引的faces
(因为如果它包含一个顶点,那么它必须是graph
的面)。我不确定这样做是否高效。如果有一个快速的 networkx 解决方法,那就太好了。
faces
和verts
。是的,我基本上想以与verts
相同的方式将面排序为对象,其中每组verts
都连接在一起。由于verts
可以放在边缘列表的形式中,因此这更容易。我不知道如何使用faces
做到这一点。我会更新帖子并添加我忘记放入的更多代码。 - McM