Редактировать: я написал более краткую версию этого вопроса здесь , но я сохраняю этот пост, потому что это полное объяснение.
Учитывая массив 3D numpy, марширующих кубов могут образовать 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)
Возвращение этого прекрасного трехмерного изображения:
Я использую алгоритм для разделения эти объекты, используя мой собственный код (см. ниже) и получают:
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)}
Теперь проблема в том, что, хотя я нашел вершины, составляющие каждый граф, у меня больше нет простого способа создания отдельные 3D-сетки для каждого объекта. Если раньше для создания me sh использовалось verts[faces]
, то не очевидно, как соотносить каждую graph
с faces
для создания трех angular сеток. Я пытался решить эту проблему, но не удалось. Например:
verts1 = verts[0:6]
faces1 = faces[0:6]
mesh = Poly3DCollection(verts1[faces1])
Это не работает. Я думаю, что ключом было бы найти лица, которые соответствуют каждому объекту. Если бы это было сделано, это могло бы работать. Например, наш первый граф включает в себя только вершины с 1 по 6. Поэтому нам нужны только faces
, которые ссылаются на эти вершины. В качестве демонстрации можно воспроизвести первый граф graph1
(без графа 2), используя:
faces1 = faces[0:8]
mesh = Poly3DCollection(verts[faces1])
# and plot like above
Если бы я мог записать не только вершины, но и их индекс, я мог бы отсортировать faces
для тех, которые относятся к этому объекту. Я объясню. Первая проблема, у меня нет показателей. Это мой способ сортировки объектов. Сначала мы создаем список строк (или edgelist), затем создаем их кортежи, а затем используем 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
). Я не уверен, насколько эффективно это будет. Я бы с удовольствием, если бы был быстрый обходной путь для сети.