【转载】泊松盘采样算法

作者:光影帽子
链接:https://www.zhihu.com/question/276554643/answer/1039095847
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
 

泊松盘采样(possion disk sampling)的特点是任何两个点的距离都不会隔得太近。

比如下图,左边是随机生成的点,右边是泊松盘采样生成的点。

算法步骤:

第一步:设定好两个点之间最近的距离r,以及采样点所在空间的维度n,比如2维平面

第二步:在空间里生成足够多的网格,保证不接触的两个网格之间的点的距离大于r,并且网格数量足够多保证每个网格至多只需装一个采样点就能满足采样数量。为了最优化,一般取网格边长为r/\sqrt{n}

第三步:随机生成一个点,再创建两个数组,第一个是处理数组,第二个是结果数组,即最终的输出数组。把这个点放进处理数组中和结果数组中。

第四步:如果处理数组非空,从中随机选择一个点,如下图的红点,并把这个点从处理数组中删除。如果处理数组是空的,直接输出结果数组并结束算法。

第五步:设定最小距离minr,比如r,最大距离maxr,比如2*r。以红点为中心生成一个圆环,如下图灰色圆环,在这个圆环中生成一个采样点,如下图蓝点。

第六步:检测这个蓝点是否与其他点隔得太近,也就是距离小于r,由于之前已经设定好了网格,所以这里只要检查9个网格里的点就行了。如上图浅蓝色点周围的绿色的网格。隔得太近的就取消这个蓝点,否则就保留并放入处理数组和结果数组中。

第七步:设定好采样次数k,比如30。如果经过k次采样后,在圆环里仍然找不到可用的新点,那么就放弃这次采样。然后重复第三步。

当然也可以魔改一下,比如根据图片的不同灰度设置不同的距离r,比如下图

### 采样算法 Python 实现 采样的目标是在给定区域中生成一组样本点,这些点之间的最小距离不小于指定半径 \( r \),从而使得分布更加均匀。 #### 使用 `numpy` 和 `matplotlib` 下面是一个简单的二维平面内实现采样的例子: ```python import numpy as np import matplotlib.pyplot as plt def poisson_disk_sampling(width, height, min_distance, k=30): # 初始化参数 cell_size = min_distance / np.sqrt(2) grid_width = int(np.ceil(width / cell_size)) grid_height = int(np.ceil(height / cell_size)) grid = {} active_list = [] def generate_point(center, radius): direction = 2 * np.pi * np.random.rand() distance = radius + (np.random.rand() * (min_distance - radius)) point = ( center[0] + distance * np.cos(direction), center[1] + distance * np.sin(direction) ) return point def is_valid(point): i, j = int(point[0] / cell_size), int(point[1] / cell_size) for di in range(-1, 2): for dj in range(-1, 2): neighbor_cell = grid.get((i + di, j + dj)) if neighbor_cell and any( ((point[0]-p[0])**2 + (point[1]-p[1])**2 < min_distance**2 for p in neighbor_cell)): return False return True first_point = width * np.random.rand(), height * np.random.rand() active_list.append(first_point) grid[(int(first_point[0]/cell_size), int(first_point[1]/cell_size))] = [first_point] while active_list: current_point = active_list.pop(np.random.randint(len(active_list))) found = False for _ in range(k): new_point = generate_point(current_point, min_distance) if all([new_point[0]>=0, new_point[0]<=width, new_point[1]>=0, new_point[1]<=height]) \ and is_valid(new_point): active_list.append(new_point) ci, cj = int(new_point[0]/cell_size), int(new_point[1]/cell_size) if not (ci, cj) in grid: grid[(ci,cj)] = [] grid[(ci,cj)].append(new_point) found = True if not found: continue result_points = sum(grid.values(), []) xs, ys = zip(*result_points) plt.scatter(xs, ys) plt.xlim(0, width) plt.ylim(0, height) plt.gca().set_aspect('equal', adjustable='box') plt.show() poisson_disk_sampling(500, 500, 20)[^1] ``` 此代码实现了基于网格加速的采样方法,在每次迭代过程中尝试从当前活动列表中的某个点出发寻找新的有效候选点。如果找到合适的点,则加入到结果集中并继续作为新起点;否则移除该点不再考虑其后续扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值