如何使用scipy.spatial.Delaunay在delaunay三角剖分中找到给定点的所有邻居?

时间:2012-09-11 17:19:12

标签: python numpy scipy triangulation delaunay

我一直在寻找这个问题的答案,但找不到任何有用的东西。

我正在使用python科学计算堆栈(scipy,numpy,matplotlib),我有一组2维点,我使用scipy.spatial.Delaunay为其计算Delaunay traingulation(wiki)。

我需要编写一个函数,给定任何点a,将返回所有其他点,这些点是任何单形(即三角形)的顶点,a也是({3}}的邻居在三角测量中a。但是,scipy.spatial.Delaunayhere)的文档非常糟糕,我不能在我的生活中理解如何指定单纯形式,或者我会这样做。即使只是解释了Delaunay输出中的neighborsverticesvertex_to_simplex数组是如何组织的,也足以让我前进。

非常感谢您的帮助。

8 个答案:

答案 0 :(得分:15)

我自己想出来了,所以这里有一个对未来被这个人感到困惑的人的解释。

作为一个例子,让我们使用我在代码中使用的简单点阵,我生成如下

import numpy as np
import itertools as it
from matplotlib import pyplot as plt
import scipy as sp

inputs = list(it.product([0,1,2],[0,1,2]))
i = 0
lattice = range(0,len(inputs))
for pair in inputs:
    lattice[i] = mksite(pair[0], pair[1])
    i = i +1

这里的细节并不重要,足以说它生成一个规则的三角形格子,其中一个点与其六个最近邻居之间的距离为1。

绘制它

plt.plot(*np.transpose(lattice), marker = 'o', ls = '')
axes().set_aspect('equal')

enter image description here

现在计算三角测量:

dela = sp.spatial.Delaunay
triang = dela(lattice)

让我们来看看这给了我们什么。

triang.points

输出:

array([[ 0.        ,  0.        ],
       [ 0.5       ,  0.8660254 ],
       [ 1.        ,  1.73205081],
       [ 1.        ,  0.        ],
       [ 1.5       ,  0.8660254 ],
       [ 2.        ,  1.73205081],
       [ 2.        ,  0.        ],
       [ 2.5       ,  0.8660254 ],
       [ 3.        ,  1.73205081]])

简单,只是上面说明的格子中所有九个点的数组。让我们看看:

triang.vertices

输出:

array([[4, 3, 6],
       [5, 4, 2],
       [1, 3, 0],
       [1, 4, 2],
       [1, 4, 3],
       [7, 4, 6],
       [7, 5, 8],
       [7, 5, 4]], dtype=int32)

在此数组中,每行代表三角测量中的一个单形(三角形)。每行中的三个条目是我们刚看到的点数组中该单形的顶点的索引。因此,例如,此数组中的第一个单形,[4, 3, 6]由点组成:

[ 1.5       ,  0.8660254 ]
[ 1.        ,  0.        ]
[ 2.        ,  0.        ]

通过在一张纸上绘制网格,根据其索引标记每个点,然后跟踪triang.vertices中的每一行,很容易看出这一点。

这是我编写我在问题中指定的函数所需的所有信息。 它看起来像:

def find_neighbors(pindex, triang):
    neighbors = list()
    for simplex in triang.vertices:
        if pindex in simplex:
            neighbors.extend([simplex[i] for i in range(len(simplex)) if simplex[i] != pindex])
            '''
            this is a one liner for if a simplex contains the point we`re interested in,
            extend the neighbors list by appending all the *other* point indices in the simplex
            '''
    #now we just have to strip out all the dulicate indices and return the neighbors list:
    return list(set(neighbors))

就是这样!我确信上面的功能可以通过一些优化来实现,这正是我在几分钟内提出的。如果有人有任何建议,请随时发布。希望这有助于将来像我一样对此感到困惑的人。

答案 1 :(得分:8)

上面描述的方法循环遍历所有单形,这可能需要很长时间,以防有大量的点。更好的方法可能是使用Delaunay.vertex_neighbor_vertices,它已包含有关邻居的所有信息。不幸的是,提取信息

def find_neighbors(pindex, triang):

    return triang.vertex_neighbor_vertices[1][triang.vertex_neighbor_vertices[0][pindex]:triang.vertex_neighbor_vertices[0][pindex+1]]

以下代码演示了如何获取某些顶点的索引(在此示例中为数字17):

import scipy.spatial
import numpy
import pylab

x_list = numpy.random.random(200)
y_list = numpy.random.random(200)

tri = scipy.spatial.Delaunay(numpy.array([[x,y] for x,y in zip(x_list, y_list)]))

pindex = 17

neighbor_indices = find_neighbors(pindex,tri)

pylab.plot(x_list, y_list, 'b.')
pylab.plot(x_list[pindex], y_list[pindex], 'dg')
pylab.plot([x_list[i] for i in neighbor_indices],
           [y_list[i] for i in neighbor_indices], 'ro')    

pylab.show()

答案 2 :(得分:3)

这也是James Porter使用列表理解的简单单行版本的答案:

find_neighbors = lambda x,triang: list(set(indx for simplex in triang.simplices if x in simplex for indx in simplex if indx !=x))

答案 3 :(得分:3)

这是关于@astrofrog答案的详细说明。这也适用于2D以上。

在3D设定的2430点(大约16000个单纯形态)上耗费了大约300毫秒。

from collections import defaultdict

def find_neighbors(tess):
    neighbors = defaultdict(set)

    for simplex in tess.simplices:
        for idx in simplex:
            other = set(simplex)
            other.remove(idx)
            neighbors[idx] = neighbors[idx].union(other)
    return neighbors

答案 4 :(得分:2)

我也需要这个,并且遇到了以下答案。事实证明,如果您需要所有初始点的邻居,那么一次性生成邻居字典会更有效(以下示例适用于2D):

def find_neighbors(tess, points):

    neighbors = {}
    for point in range(points.shape[0]):
        neighbors[point] = []

    for simplex in tess.simplices:
        neighbors[simplex[0]] += [simplex[1],simplex[2]]
        neighbors[simplex[1]] += [simplex[2],simplex[0]]
        neighbors[simplex[2]] += [simplex[0],simplex[1]]

    return neighbors

v的邻居然后是neighbors[v]。对于10,000点,这需要370毫秒才能在我的笔记本电脑上运行。也许其他人有进一步优化的想法?

答案 5 :(得分:0)

这里的所有答案都集中在获取邻居一分上(astrofrog除外,但这是二维的,并且快6倍),但是,获取同样昂贵所有点→所有邻居的映射

您可以使用

from collections import defaultdict
from itertools import permutations
tri = Delaunay(...)
_neighbors = defaultdict(set)
for simplex in tri.vertices:
    for i, j in permutations(simplex, 2):
        _neighbors[i].add(j)

points = [tuple(p) for p in tri.points]
neighbors = {}
for k, v in _neighbors.items():
    neighbors[points[k]] = [points[i] for i in v]

这在任何维度上都有效,并且此解决方案找到所有点的所有邻居的速度比仅找到一个点的邻居(James Porter的例外答案)要快

答案 6 :(得分:0)

我知道距提出这个问题已经有一段时间了。但是,我只是遇到了同样的问题,并想出了解决方法。只需使用Delaunay三角剖分对象的(有点文献记载)“ vertex_neighbor_vertices”方法(我们称其为“ tri”)即可。 它将返回两个数组:

def get_neighbor_vertex_ids_from_vertex_id(vertex_id,tri):
    #use a less awful function name
    helper = tri.vertex_neighbor_vertices
    index_pointers = helper[0]
    indices = helper[1]
    result_ids = indices[index_pointers[vertex_id]:index_pointers[vertex_id+1]]

    return result_ids

具有顶点vertex_id的点的相邻顶点存储在我命名为“ indices”的第二个数组中的某个位置。但是哪里?这是第一个数组(我称为“ index_pointers”)进入的位置。(第二个数组“ indices”的起始位置)是index_pointers [vertex_id],经过相关子数组的第一个位置是index_pointers [vertex_id + 1] ]。所以解决方案是索引[index_pointers [vertex_id]:index_pointers [vertex_id + 1]]

答案 7 :(得分:0)

我们可以找到一个包含顶点 (tri.vertex_to_simplex[vertex]) 的单纯形,然后递归搜索这个单纯形的邻居 (tri.neighbors) 以找到包含该顶点的其他单纯形。

from scipy.spatial import Delaunay
tri = Delaunay(points)  #points is the list of input points
neighbors =[]           #array of neighbors for all vertices

for i in range(points):

    vertex = i            #vertex index
    vertexneighbors = []  #array of neighbors for vertex i
    neighbour1 = -1
    neighbour2=-1
    firstneighbour=-1
    neighbour1index = -1
    currentsimplexno= tri.vertex_to_simplex[vertex]
    for i in range(0,3):
        if (tri.simplices[currentsimplexno][i]==vertex):
            firstneighbour=tri.simplices[currentsimplexno][(i+1) % 3]
            vertexneighbors.append(firstneighbour)
            neighbour1index=(i+1) % 3
            neighbour1=tri.simplices[currentsimplexno][(i+1) % 3]
            neighbour2=tri.simplices[currentsimplexno][(i+2) % 3]
    while (neighbour2!=firstneighbour):
        vertexneighbors.append(neighbour2)
        currentsimplexno= tri.neighbors[currentsimplexno][neighbour1index]
        for i in range(0,3):
            if (tri.simplices[currentsimplexno][i]==vertex):
                neighbour1index=(i+1) % 3
                neighbour1=tri.simplices[currentsimplexno][(i+1) % 3]
                neighbour2=tri.simplices[currentsimplexno][(i+2) % 3]
    neighbors.append(vertexneighbors)
print (neighbors)