Python:从Scipy计算Voronoi细分';三维s-Delaunay三角剖分
我有大约50000个3D数据点,我从新的scipy(我使用0.10)运行了scipy.spatial.Delaunay,这给了我一个非常有用的三角剖分 基于:(第“与沃罗诺图的关系”一节) …我想知道是否有一种简单的方法可以得到这个三角剖分的“对偶图”,即Voronoi细分 有什么线索吗?我在这方面的搜索似乎没有显示任何预先构建的scipy函数,这让我觉得很奇怪 谢谢,Python:从Scipy计算Voronoi细分';三维s-Delaunay三角剖分,python,3d,scipy,delaunay,voronoi,Python,3d,Scipy,Delaunay,Voronoi,我有大约50000个3D数据点,我从新的scipy(我使用0.10)运行了scipy.spatial.Delaunay,这给了我一个非常有用的三角剖分 基于:(第“与沃罗诺图的关系”一节) …我想知道是否有一种简单的方法可以得到这个三角剖分的“对偶图”,即Voronoi细分 有什么线索吗?我在这方面的搜索似乎没有显示任何预先构建的scipy函数,这让我觉得很奇怪 谢谢, 爱德华我不知道有什么函数可以做到这一点,但这似乎不是一项过于复杂的任务 正如维基百科文章所描述的,沃罗诺图是两个外圆的连接点
爱德华我不知道有什么函数可以做到这一点,但这似乎不是一项过于复杂的任务 正如维基百科文章所描述的,沃罗诺图是两个外圆的连接点 你可以从一个函数开始,找到三角形外接圆的中心,这是基础数学(http://en.wikipedia.org/wiki/Circumscribed_circle)
然后,只需连接相邻三角形的中心。可以在Delaunay对象的
邻居属性中找到邻接信息。不幸的是,代码目前没有向用户公开环中心,因此您必须自己重新计算这些环中心
此外,扩展到无穷远的Voronoi边也不是通过这种方式直接获得的。这仍然是可能的,但需要更多的思考
import numpy as np
from scipy.spatial import Delaunay
points = np.random.rand(30, 2)
tri = Delaunay(points)
p = tri.points[tri.vertices]
# Triangle vertices
A = p[:,0,:].T
B = p[:,1,:].T
C = p[:,2,:].T
# See http://en.wikipedia.org/wiki/Circumscribed_circle#Circumscribed_circles_of_triangles
# The following is just a direct transcription of the formula there
a = A - C
b = B - C
def dot2(u, v):
return u[0]*v[0] + u[1]*v[1]
def cross2(u, v, w):
"""u x (v x w)"""
return dot2(u, w)*v - dot2(u, v)*w
def ncross2(u, v):
"""|| u x v ||^2"""
return sq2(u)*sq2(v) - dot2(u, v)**2
def sq2(u):
return dot2(u, u)
cc = cross2(sq2(a) * b - sq2(b) * a, a, b) / (2*ncross2(a, b)) + C
# Grab the Voronoi edges
vc = cc[:,tri.neighbors]
vc[:,tri.neighbors == -1] = np.nan # edges at infinity, plotting those would need more work...
lines = []
lines.extend(zip(cc.T, vc[:,:,0].T))
lines.extend(zip(cc.T, vc[:,:,1].T))
lines.extend(zip(cc.T, vc[:,:,2].T))
# Plot it
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
lines = LineCollection(lines, edgecolor='k')
plt.hold(1)
plt.plot(points[:,0], points[:,1], '.')
plt.plot(cc[0], cc[1], '*')
plt.gca().add_collection(lines)
plt.axis('equal')
plt.xlim(-0.1, 1.1)
plt.ylim(-0.1, 1.1)
plt.show()
我遇到了同样的问题,并根据pv的答案和我在网上找到的其他代码片段构建了一个解决方案。该解决方案返回完整的Voronoi图,包括不存在三角形邻域的外线
#!/usr/bin/env python
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.spatial import Delaunay
def voronoi(P):
delauny = Delaunay(P)
triangles = delauny.points[delauny.vertices]
lines = []
# Triangle vertices
A = triangles[:, 0]
B = triangles[:, 1]
C = triangles[:, 2]
lines.extend(zip(A, B))
lines.extend(zip(B, C))
lines.extend(zip(C, A))
lines = matplotlib.collections.LineCollection(lines, color='r')
plt.gca().add_collection(lines)
circum_centers = np.array([triangle_csc(tri) for tri in triangles])
segments = []
for i, triangle in enumerate(triangles):
circum_center = circum_centers[i]
for j, neighbor in enumerate(delauny.neighbors[i]):
if neighbor != -1:
segments.append((circum_center, circum_centers[neighbor]))
else:
ps = triangle[(j+1)%3] - triangle[(j-1)%3]
ps = np.array((ps[1], -ps[0]))
middle = (triangle[(j+1)%3] + triangle[(j-1)%3]) * 0.5
di = middle - triangle[j]
ps /= np.linalg.norm(ps)
di /= np.linalg.norm(di)
if np.dot(di, ps) < 0.0:
ps *= -1000.0
else:
ps *= 1000.0
segments.append((circum_center, circum_center + ps))
return segments
def triangle_csc(pts):
rows, cols = pts.shape
A = np.bmat([[2 * np.dot(pts, pts.T), np.ones((rows, 1))],
[np.ones((1, rows)), np.zeros((1, 1))]])
b = np.hstack((np.sum(pts * pts, axis=1), np.ones((1))))
x = np.linalg.solve(A,b)
bary_coords = x[:-1]
return np.sum(pts * np.tile(bary_coords.reshape((pts.shape[0], 1)), (1, pts.shape[1])), axis=0)
if __name__ == '__main__':
P = np.random.random((300,2))
X,Y = P[:,0],P[:,1]
fig = plt.figure(figsize=(4.5,4.5))
axes = plt.subplot(1,1,1)
plt.scatter(X, Y, marker='.')
plt.axis([-0.05,1.05,-0.05,1.05])
segments = voronoi(P)
lines = matplotlib.collections.LineCollection(segments, color='k')
axes.add_collection(lines)
plt.axis([-0.05,1.05,-0.05,1.05])
plt.show()
#/usr/bin/env python
将numpy作为np导入
导入matplotlib
将matplotlib.pyplot作为plt导入
从scipy.spatial导入Delaunay
德夫沃罗诺(P):
delauny=Delaunay(P)
三角形=delauny.points[delauny.vertices]
行=[]
#三角形顶点
A=三角形[:,0]
B=三角形[:,1]
C=三角形[:,2]
线。延伸(拉链(A,B))
线。延伸(拉链(B,C))
行。扩展(压缩(C,A))
lines=matplotlib.collections.LineCollection(行,color='r')
plt.gca().add_集合(行)
圆周中心=np.数组([三角形中的三角形的三角形csc(tri)])
段=[]
对于i,枚举中的三角形(三角形):
圆周中心=圆周中心[i]
对于j,枚举中的邻居(delauny.neights[i]):
如果是邻居!=-1:
segments.append((环绕中心,环绕中心[邻居])
其他:
ps=三角形[(j+1)%3]-三角形[(j-1)%3]
ps=np.array((ps[1],-ps[0]))
中间=(三角形[(j+1)%3]+三角形[(j-1)%3])*0.5
di=中间三角形[j]
ps/=np.linalg.norm(ps)
di/=np.linalg.norm(di)
如果np.dot(di,ps)<0.0:
ps*=-1000.0
其他:
ps*=1000.0
段。附加((环绕中心,环绕中心+ps))
返回段
def triangle_csc(pts):
行,列=pts.shape
A=np.bmat([[2*np.dot(pts,pts.T),np.one((行,1))],
[np.one((1,行)),np.zero((1,1))]]
b=np.hstack((np.sum(pts*pts,axis=1),np.one((1)))
x=np.linalg.solve(A,b)
bary_coords=x[:-1]
返回np.sum(pts*np.tile(bary_coords.reformate((pts.shape[0],1)),(1,pts.shape[1]),轴=0)
如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
P=np.随机.随机((300,2))
十、 Y=P[:,0],P[:,1]
图=零件图(图尺寸=(4.5,4.5))
轴=plt.子批次(1,1,1)
plt.散射(X,Y,标记='。)
plt.轴([-0.05,1.05,-0.05,1.05])
分段=voronoi(P)
lines=matplotlib.collections.LineCollection(线段,color='k')
轴。添加_集合(行)
plt.轴([-0.05,1.05,-0.05,1.05])
plt.show()
黑线=Voronoi图,红线=Delauny三角形
由于我在这方面花费了大量时间,我想与大家分享我的解决方案,了解如何获得Voronoi多边形,而不仅仅是边
该代码位于,并在的解决方案上进行了扩展
首先,我修改了代码,分别为我提供顶点和(对)索引(=边),因为在处理索引而不是点坐标时,可以简化许多计算
然后,在voronoi\u cell\u line
方法中,我确定哪些边属于哪些单元格。为此,我使用了一个相关问题的建议解决方案。也就是说,对于每条边,找到两个最近的输入点(=单元格),并从中创建映射
最后一步是创建实际多边形(请参见voronoi\u polygons
method)。首先,需要关闭边缘悬垂的外部单元。这就像查看所有边并检查哪些边只有一条相邻边一样简单。可以有零个或两个这样的边。在两个例子中,我通过引入一条额外的边来连接它们
最后,需要将每个单元中无序的边按正确的顺序排列,以便从中导出多边形
用法是:
P = np.random.random((100,2))
fig = plt.figure(figsize=(4.5,4.5))
axes = plt.subplot(1,1,1)
plt.axis([-0.05,1.05,-0.05,1.05])
vertices, lineIndices = voronoi(P)
cells = voronoi_cell_lines(P, vertices, lineIndices)
polys = voronoi_polygons(cells)
for pIdx, polyIndices in polys.items():
poly = vertices[np.asarray(polyIndices)]
p = matplotlib.patches.Polygon(poly, facecolor=np.random.rand(3,1))
axes.add_patch(p)
X,Y = P[:,0],P[:,1]
plt.scatter(X, Y, marker='.', zorder=2)
plt.axis([-0.05,1.05,-0.05,1.05])
plt.show()
哪些产出:
该代码可能不适合大量的输入点,在某些方面可以改进。尽管如此,这可能对其他有类似问题的人有所帮助。请再次回到这里,这是一个非常好的答案,非常感谢+1.谢谢你的代码ncross2
采用u
和v
作为参数,但计算的值仅取决于a
和b
。也许a
和b
应该替换为u
和v
?使用凸包属性可以很容易地找到无穷大的边。如果需要,我可以发布代码。@afaulconbridge您希望输出的外观如何,每个最外面的面上的向量?@meawoppl理想情况下是与提供的外部多边形相交的点,但我可以通过向量计算出来。100%可能。也k