【问题标题】:How to get uniformly distributed points in convex hull?如何在凸包中获得均匀分布的点?
【发布时间】:2020-03-23 05:32:43
【问题描述】:

给定一组点,

points = np.random.randn(...) # n 3d points

我想用 3d 点列表(形状为 nx3 的 np.array)均匀地填充由凸包定义的体积。

我可以得到凸包

hull = scipy.spatial.ConvexHull(points)

获得均匀填充该船体体积的点列表的最快方法是什么?

【问题讨论】:

  • 泊松圆盘采样怎么样?

标签: python numpy scipy computational-geometry convex-hull


【解决方案1】:
  1. 求船体的 delaunay 单纯形

  2. 根据面积随机抽样单纯形

  3. 对于每个单纯形,使用dirichelet 分布找到采样点的均匀分布

  4. 将分布与单纯形相乘以找到最终点。

    import numpy as np
    from numpy.linalg import det
    from scipy.stats import dirichlet
    
    
    def dist_in_hull(points, n):
        dims = points.shape[-1]
        hull = points[ConvexHull(points).vertices]
        deln = hull[Delaunay(hull).simplices]
    
        vols = np.abs(det(deln[:, :dims, :] - deln[:, dims:, :])) / np.math.factorial(dims)    
        sample = np.random.choice(len(vols), size = n, p = vols / vols.sum())
    
        return np.einsum('ijk, ij -> ik', deln[sample], dirichlet.rvs([1]*(dims + 1), size = n))
    
    

编辑:功能化并扩展到更高维度(警告:ConvexHull 仅适用于 9D)

【讨论】:

  • 我试过你的答案,但它对我不起作用(见image)。
【解决方案2】:

在边界框中均匀地绘制点,并拒绝那些不在船体内部的点。 (由于外壳是凸面的,这可以在线性时间 O(F) 内完成,无需预处理,在预处理后的对数时间 O(log F) 内完成,将面投影到平面并考虑平面细分)。

【讨论】:

  • 对不起,我不明白该怎么做。什么是F?什么预处理?我应该将哪个平面投影到哪个指向,为什么这样就足够了
  • @Gulzar:F 是面数。您可以在任何平面上投影。看看en.wikipedia.org/wiki/Point_location
  • 哦。是的,我已经实施了。我认为这可以更快地完成,因为实际上并非所有点都必须分类为输入/输出。你如何获得 log(f)?
  • 这有点危险,因为您可能需要生成非常大量的点。除非您旋转轴以匹配船体的主轴,否则您的船体与其边界框相比可以任意小(想象一个由围绕线 x=y=z 旋转的细椭圆表示的船体)并且您最终可能会拒绝您生成的大部分积分。诚然,使用轴变换和 3d,您的最坏情况拒绝是 pi/6 = 52%,但是随着您的维数变高,您最坏情况的拒绝将逐渐变得更糟。 4d = pi^2/32 = 30%, 5d: 16%, 6d: 8%
  • @DanielF:据我所知,3 并不是一个增长的数字。您当然可以找到病态案例,即使在 2D 中,效率也是 0%。我的回答是出于实际目的,尽管有效的解决方案并不容易实施,而且只有在面孔数量允许的情况下才有意义。
【解决方案3】:

如果它对某人有帮助,这里是实现 Yves 答案的代码:

  1. 在凸包的边界框中均匀地绘制点。
  2. 拒绝落在外壳外的点。
import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
from matplotlib.patches import Rectangle
from matplotlib.path import Path

#Initial positions
pos = np.random.uniform(-30, 10, (20, 2))

#Convex hull of initial positions
hull = ConvexHull( pos )

#Bounding box
bbox = [hull.min_bound, hull.max_bound]

#Hull path
hull_path = Path( hull.points[hull.vertices] )

#Draw n random points inside the convex hull
n = 10**3
rand_points = np.empty((n, 2))
for i in range(n):
    #Draw a random point in the bounding box of the convex hull
    rand_points[i] = np.array([np.random.uniform(bbox[0][0], bbox[1][0]), np.random.uniform(bbox[0][1], bbox[1][1])])

    #We check if the random point is inside the convex hull, otherwise we draw it again            
    while hull_path.contains_point(rand_points[i]) == False:
        rand_points[i] = np.array([np.random.uniform(bbox[0][0], bbox[1][0]), np.random.uniform(bbox[0][1], bbox[1][1])])

#Plot
plt.scatter(pos[:, 0],pos[:, 1], marker='o',  c='blue', alpha = 1, label ='Initial points')
for simplex in hull.simplices:
        plt.plot(hull.points[simplex, 0], hull.points[simplex, 1], '-k')
plt.gca().add_patch(Rectangle((bbox[0][0], bbox[0][1]), bbox[1][0] - bbox[0][0], bbox[1][1] - bbox[0][1],facecolor = 'None', edgecolor = 'cyan'))
plt.scatter(rand_points[:, 0],rand_points[:, 1], marker='o',  c='red', alpha = 0.31, label ='Random points inside hull')
plt.legend()
plt.savefig("fig.png", dpi = 300)

【讨论】:

  • 这是未矢量化的,会很慢。你能做一个矢量化的版本吗?按原样,这是不可用的。
  • vectorize @Gulzar 是什么意思?我不是程序员,我不深入了解python,但我需要做这个算法,我设法让它工作,所以我决定分享它。如果你能指出我怎样才能让它变得更好,我会改变它。
  • 我不能真正教所有相关的东西,但如果你感兴趣,从这里开始realpython.com/numpy-array-programming
  • 点是 Python 循环(非常)慢。 Numpy 向量操作在 c 中实现,并具有多种底层优化。生产级代码必须使用它们。
  • @Gulzar 如果我能设法用列表理解向量化 for 循环,我看了一点,但由于 while 循环,我没有管理。如果有人能想到更好的方法,我会很乐意改进答案。无论如何,这个算法在我的特定问题上有效,我相信它仍然可以帮助其他人。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-02
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多