Given a set of triangle vertices and faces, separate objects and form separate meshes

点点圈 提交于 2020-06-23 08:52:25

问题


Edit: I have written a more succinct version of this question here but I am keeping this post because it is a full explanation.

Given a 3D numpy array, marching cubes can form a 3D object around some threshold.

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]]

This can be plotted:

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)

Returning this lovely 3D image:

Tetrahedrons

I use an algorithm to separate these objects using my own code (see below) and get:

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)}

Now, the problem is that, even though I have found the vertices composing each graph, I no longer have the an easy way to create separate 3D meshes for each object. Whereas before, verts[faces] was used to create a mesh, it is not obvious how to relate each graph to faces to create triangular meshes. I've tried to solve this but have not been successful. For example:

verts1 = verts[0:6]
faces1 = faces[0:6] 
mesh = Poly3DCollection(verts1[faces1])

This does not work. I think the key would be to find the faces that correspond to each object. If that were done, it might work. For example, our first graph includes only vertices 1 through 6. So we only need the faces that refer to those vertices. As a demonstration, the first graph, graph1 can be reproduced (without graph2) using:

faces1 = faces[0:8]
mesh = Poly3DCollection(verts[faces1])
# and plot like above

If I could record which not only the vertices, but their index, then I might be able to sort faces for those which refer to that object. I'll explain. First problem, I do not have the indices. This is my way of sorting objects. We first create a linelist (or edgelist), then we make tuples of them, and then use networkx to find connected components.

# 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

I do not see how networkx could also record the index of each vertex.

Secondly, it's possible that the faces referring to each object are disjoint, i.e. it may be faces[0:4] + faces[66] + faces[100:110]. However, that can likely be overcome.

Assuming that we can generate a list of indices for each graph, the main problem is discovering an efficient way of discovering which faces refer to those vertices. My solution works for this set of objects, but not for more complicated arrangements (which I can provide). It is also extraordinarily slow. Still, here it is:

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())

If you have read this far, I am truly impressed with the community. I think there is an efficient way of doing this perhaps with networkx, but I have not found it.

Desired output: I want to sort the faces into connected components just as I sort the verts. graph1 = faces[x1] + faces[x2] + ... + faces[xn].

Edit: If someone could help me with the coding, I do have an idea (thanks in part to @Ehsan). After separating into connected components and finding the graphs, the vertices of each could be hashed to find original index. Then, one might be able to search for faces which include at least one of those indices (since if it contains one vertex, it must be a face of the graph). I'm not sure how efficient this would be. I'd love if there were a fast networkx workaround.


回答1:


@Paul Broderson answered this question https://stackoverflow.com/a/61590348/12919727

I will put it here just for aesthetics:

#!/usr/bin/env python
"""
Given a list of triangles, find the connected components.

https://stackoverflow.com/q/61584283/2912349
"""
import itertools
import networkx as nx

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]]

#create graph
edges = []
for face in faces:
    edges.extend(list(itertools.combinations(face, 2)))
g = nx.from_edgelist(edges)

# compute connected components and print results
components = list(nx.algorithms.components.connected_components(g))

for component in components:
    print(component)

# {0, 1, 2, 3, 4, 5}
# {6, 7, 8, 9, 10, 11}

# separate faces by component
component_to_faces = dict()
for component in components:
    component_to_faces[tuple(component)] = [face for face in faces if set(face) <= component] # <= operator tests for subset relation

for component, component_faces in component_to_faces.items():
    print(component, component_faces)
# (0, 1, 2, 3, 4, 5) [[2, 1, 0], [0, 3, 2], [1, 4, 0], [0, 4, 3], [5, 1, 2], [3, 5, 2], [5, 4, 1], [4, 5, 3]]
# (6, 7, 8, 9, 10, 11) [[8, 7, 6], [6, 9, 8], [7, 10, 6], [6, 10, 9], [11, 7, 8], [9, 11, 8], [11, 10, 7], [10, 11, 9]] 


来源:https://stackoverflow.com/questions/61569196/given-a-set-of-triangle-vertices-and-faces-separate-objects-and-form-separate-m

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!