Generalizion 3D位置磁盘采样

时间:2019-06-25 08:02:39

标签: python poisson sample-data

我想为3d网格创建一个Poisson磁盘采样。我使用https://github.com/emulbreh/bridson实现将其概括为3D。但是,似乎我缺少了一些东西,但找不到问题。此实现遵循Robert Bridson的算法(https://www.cs.ubc.ca/~rbridson/docs/bridson-siggraph07-poissondisk.pdf

我要做的是将每个2d对象变成3d,并使用球坐标创建新点。

from random import random
from math import cos, sin, floor, sqrt, pi, ceil


def euclidean_distance(a, b):
    dx = a[0] - b[0]
    dy = a[1] - b[1]
    dz = a[2] - b[2]

    return sqrt(dx * dx + dy * dy + dz * dz)


def poisson_disc_samples(width, height,thick, r, k=5, distance=euclidean_distance, random=random):
    tau = 2 * pi
    cellsize = r / sqrt(2)

    grid_width = int(ceil(width / cellsize))
    grid_height = int(ceil(height / cellsize))
    grid_thick = int(ceil(thick / cellsize))

    grid = [None] * (grid_width * grid_height * grid_thick)
    def grid_coords(p):
        return int(floor(p[0] / cellsize)), int(floor(p[1] / cellsize)), int(floor(p[2] / cellsize))

    def fits(p, gx, gy, gz):
        yrange = list(range(max(gy - 2, 0), min(gy + 3, grid_height)))
        zrange = list(range(max(gz - 2, 0), min(gz + 3, grid_thick)))
        for x in range(max(gx - 2, 0), min(gx + 3, grid_width)):
            for y in yrange:
                for z in zrange:
                    g = grid[x + y + z  * grid_width]
                    if g is None:
                        continue
                    if distance(p, g) <= r:
                        return False
        return True
    p = width * random(), height * random(), thick * random()
    queue = [p]
    grid_x, grid_y, grid_z = grid_coords(p)
    grid[grid_x + grid_y + grid_z * grid_width] = p

    while queue:
        qi = int(random() * len(queue))
        qx, qy, qz = queue[qi]
        queue[qi] = queue[-1]
        queue.pop()
        for _ in range(k):
            alpha = tau * random()
            theta = tau * random()
            d = r * sqrt(3 * random() + 1)
            px = qx + d * cos(alpha) * sin(theta)
            py = qy + d * sin(alpha) * sin(theta)
            pz = qz + d * cos(theta)
            if not (0 <= px < width and 0 <= py < height):
                continue
            p = (px, py, pz)
            grid_x, grid_y, grid_z = grid_coords(p)
            if not fits(p, grid_x, grid_y,grid_z):
                continue
            queue.append(p)
            grid[grid_x + grid_y + grid_z * grid_width] = p
    return [p for p in grid if p is not None]

r = 10
samples = poisson_disc_samples(100, 100, 100, r=10, random=random)

print(samples)

我正在寻找的结果只是打印采样的3D点,但是该程序不会打印任何错误,我感觉是因为它找不到采样点,随后计算机崩溃了。

我认为可能是导致问题的函数fits(负责检查网格是否为空的函数)。

2 个答案:

答案 0 :(得分:1)

我认为您对代码的更改很好...我对原代码的唯一担心是调用queue.pop()而不是break在发现某个点时退出循环看起来有点可疑。如果您关心样本的发出顺序,那么这将与已发布的算法不同,但是否则可能无关紧要

您的主要问题是试图生成很多点,并且该方法必须为每个点做大量工作,因此需要一段时间才能完成!如果我在生成代码时将代码更改为yield个点(即在yield p附近添加queue.append(p)),而不是只在最后将它们全部返回,那么我会得到看起来合理的输出< / p>

答案 1 :(得分:0)

我偶然通过谷歌搜索此代码的python 3d实现的代码。发布的代码确实不起作用!至少有两个问题:

    grid[grid_x + grid_y + grid_z * grid_width] = p

在此行(和类似的行)中,索引未正确索引网格,该网格应为3d数组(或替换3d数组的列表)。最简单的解决方案是使用一个实际的numpy 3d数组并将索引存储到其中的点的平面列表中(这不是本文建议的内容)

d = r * sqrt(3 * random() + 1)
px = qx + d * cos(alpha) * sin(theta)
py = qy + d * sin(alpha) * sin(theta)
pz = qz + d * cos(theta)

这应该在三个维度上均匀地采样r和2r之间的样本,但是我相信这些样本将偏向更小的距离。