问题
My algorithm outputs the set of vertices describing objects in 3D space (x, y, z). In this case, there are two objects:
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. ]]
There are two tetrahedrons, one confined between centered on (1, 1, 1), the other on (8, 8, 8). My goal is to use breadth-first search to identify that the objects are separate, and then classify each. I have not been able to get the data in the correct form for my algorithm.
Instead, I intend to use the networkx module, specifically using the Graph class, which takes ndarrays as input. I have tried:
import networkx as nx
import numpy as np
graph = Graph(verts)
for idx, graph in enumerate(nx.connected_components(graph)):
print("Graph ",idx, " in ", graph,'\n\n',file=open("output.txt","a"))
However, I cannot create graph. Instead, I get the error:
"Input is not a correct numpy matrix or array.")
networkx.exception.NetworkXError: Input is not a correct numpy matrix or array.
This confuses me because type of verts = numpy.ndarray.
I am open to either using networkx for this task, or developing some other strategy. Additionally, please let me know if there are any edits that might make this post more clear.
Edit: One thing that may help is another output, faces. These 'define triangular faces via referencing vertex indices from verts.' I believe these can be used to 'connect' or draw lines from vertex to vertex, eventually to create a dictionary.
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]]
A method has been proposed, and it works for this set of data. However, it does not work for all. This edit uploads a new set of data.
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. ]
[3.1 1. 4. ] [4. 1. 3.1] [4. 0.1 4. ] [4. 1. 4.9] [4. 1.9 4. ] [5. 1. 3.1]
[5. 0.1 4. ] [5. 1. 4.9] [5. 1.9 4. ] [5.9 1. 4. ] [7.1 8. 8. ]
[8. 8. 7.1] [8. 7.1 8. ] [8. 8. 8.9] [8. 8.9 8. ] [9. 8. 7.1]
[9. 7.1 8. ] [9. 8. 8.9] [9. 8.9 8. ] [9.9 8. 8. ]]
And it looks like this.
回答1:
The problem is how you're constructing the graph. You should first create a new instance of a graph with g = nx.Graph()
, and then use its methods to either add its nodes or edges. In this case, you want to add its paths from the nested list:
G = nx.Graph()
for path in verts:
nx.add_path(G, path)
And then obtain the connected components:
cc = list(nx.connected_components(G))
# [{0.1, 1.0, 1.9}, {7.1, 8.0, 8.9}]
Now if you wanted to find which component each path belongs to, you could iterate over the paths and check with which of the components they intersect:
from collections import defaultdict
subgraphs = defaultdict(list)
for path in verts:
for ix,c in enumerate(cc):
if c.intersection(path):
subgraphs[ix].append(path)
print(subgraphs)
defaultdict(list,
{0: [[0.1, 1.0, 1.0],
[1.0, 1.0, 0.1],
[1.0, 0.1, 1.0],
[1.0, 1.0, 1.9],
[1.0, 1.9, 1.0],
[1.9, 1.0, 1.0]],
1: [[7.1, 8.0, 8.0],
[8.0, 8.0, 7.1],
[8.0, 7.1, 8.0],
[8.0, 8.0, 8.9],
[8.0, 8.9, 8.0],
[8.9, 8.0, 8.0]]})
回答2:
I was able to answer this by another approach. It is lengthy because I need to include extra pieces. As a general outlook, I solved this problem by utilizing faces
, which defines each triangle with the indices of its vertices. faces
tells me which vertices are connected. This allowed me to build a linelist, which contains all of the connections between vertices.
# using faces and verts in original post
linelist = []
for idx, vert in enumerate(faces):
print(vert)
for i,x in enumerate(vert):
l = [np.ndarray.tolist(verts[faces[idx][i]]), np.ndarray.tolist(verts[faces[idx][(i+1)%len(vert)]])]
linelist.append(l)
Which yields elements like:
[[1.0, 0.10000000149011612, 1.0], [1.0, 1.0, 0.10000000149011612]]
Edit: Discovered faster method:
tmp = [tuple(tuple(j) for j in i) for i in linelist]
graph = nx.Graph(tmp)
graphs = []
i=0
open('output.txt','w').close()
for idx, graph in enumerate(nx.connected_components(graph)):
graphs.append(graph)
print("Graph ",idx," corresponds to vertices: ",graph,'\n\n',file=open("output.txt","a"))
i+=1
These points are connected. Next, I used someone else's code to create a dictionary where each key is a vertex and each value is a connected vertex. And then I used breath-first-search on this dictionary. See the class below.
class MS_Graph():
def __init__ (self, linelist=None, vertices=None):
self.linelist = linelist if linelist is not None else None
self.vertices = vertices if vertices is not None else None
def getGraph(self):
'''
Takes self.linelist and converts to dict
'''
linelist = self.linelist
# edge list usually reads v1 -> v2
graph = {}
# however these are lines so symmetry is assumed
for l in linelist:
v1, v2 = map(tuple, l)
graph[v1] = graph.get(v1, ()) + (v2,)
graph[v2] = graph.get(v2, ()) + (v1,)
return graph
def BFS(self, graph):
"""
Implement breadth-first search
"""
# get nodes
#nodes = list(graph.keys()) # changed 4/16/2020
nodes = list(graph)
graphs = []
# check all nodes
while nodes:
# initialize BFS
toCheck = [nodes[0]]
discovered = []
# run bfs
while toCheck:
startNode = toCheck.pop()
for neighbor in graph.get(startNode):
if neighbor not in discovered:
discovered.append(neighbor)
toCheck.append(neighbor)
nodes.remove(neighbor)
# add discovered graphs
graphs.append(discovered)
self.graphs = graphs
return graphs
And, bringing it altogether:
Graph = MS_Graph(linelist)
graph = Graph.getGraph()
graphs = Graph.BFS(graph)
print(len(graphs))
# output: 3
print(graphs)
# output:
[[(1.0, 1.0, 0.10000000149011612), (0.10000000149011612, 1.0, 1.0), (1.0, 1.0, 1.899999976158142), (1.899999976158142, 1.0, 1.0), (1.0, 0.10000000149011612, 1.0), (1.0, 1.899999976158142, 1.0)],
[(4.0, 1.0, 3.0999999046325684), (3.0999999046325684, 1.0, 4.0), (4.0, 1.0, 4.900000095367432), (5.0, 1.0, 3.0999999046325684), (5.0, 0.10000000149011612, 4.0), (4.0, 0.10000000149011612, 4.0), (5.0, 1.0, 4.900000095367432), (5.900000095367432, 1.0, 4.0), (5.0, 1.899999976158142, 4.0), (4.0, 1.899999976158142, 4.0)],
[(8.0, 8.0, 7.099999904632568), (7.099999904632568, 8.0, 8.0), (8.0, 8.0, 8.899999618530273), (8.899999618530273, 8.0, 8.0), (8.0, 7.099999904632568, 8.0), (8.0, 8.899999618530273, 8.0)]]
That said, I do wonder if there is a faster method.
Edit: There may be a faster way. Since faces
contains the vertices of every single triangle, all triangles that belong to one object will have an unbroken chain. i.e. the set of vertices composing object 1 will be distinct from the set of vertices composing any other object.
For example the set of faces for each object:
object_1_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]
object_2_faces =
[ 8 7 6]
[ 6 9 8]
[ 7 10 6]
[ 6 10 9]
[11 7 8]
[ 9 11 8]
[11 10 7]
[10 11 9]
object_1_vertices = {0,1,2,3,4,5}
object_2_vertices = {6,7,8,9,10,11}
I imagine this means there is a faster way than finding all of the lines.
来源:https://stackoverflow.com/questions/61280483/how-to-create-a-networkx-graph-using-2d-np-array-as-input