快速 2D 有符号距离

Posted

技术标签:

【中文标题】快速 2D 有符号距离【英文标题】:Fast 2D signed distance 【发布时间】:2021-09-11 16:00:18 【问题描述】:

我需要一种方法来计算点与多边形边界之间的距离。

如果点在多边形之外,距离为正 如果点在多边形内,距离将为负数

这被称为Signed Distance Field/Function的SDF

多边形本身由多条路径组成,可以是凹的,有孔,但不能自相交,并且有很多顺时针排列的点(10000+)。

我找到了一些existing solutions,但他们需要针对每个多边形边缘测试该点,这不够有效。

这是产生的视觉结果(绿色为正,红色为负):

所以我尝试了以下方法:

将多边形边放在四叉树中

要计算距离,请找到离该点最近的边,并根据该点在边的哪一侧更改符号。

遗憾的是,当点与多个边缘(例如角)的距离相同时,它不起作用。

我已经尝试添加条件,如果一个点位于所有边的外侧,则它位于多边形之外,但它不能解决内部问题,反之亦然。

想不通...

如果有人好奇,这个想法是稍后使用一些着色器来生成这样的图像:

编辑

为了澄清,这里是拐角处出现的问题的特写:

对于区域A中的所有点,最近的段是S1,所以没问题 对于区域E中的所有点,最近的段是S2,所以也没有问题 区域 B、C 和 D 中的所有点与 S1 和 S2 的距离相同 区域 B 中的点位于 S1 的外侧和 S2 的内侧 区域 D 中的点位于 S1 的内侧和 S2 的外侧 区域 C 中的点位于两个段的外侧

人们可能会认为一个点必须位于两个段的内侧才能被视为“在”。它解决了角度 180°

最糟糕的是,两个或多个角可以共享相同的位置(例如第一张图像下部的四个角)...

【问题讨论】:

看看Adaptively Sampled Distance Fields。我似乎记得他们的一篇论文包括他们的八叉树实现的示例源代码。 孔是逆时针排列的吗? 是的,你已经说过了。但我希望你回答我的问题。 好的。我认为你的四叉树是要走的路。你说你在角落里发现了问题。不应该这样(查看您的代码)。也许如果你打破一条线,使每一块都适合树中唯一的四边形,那么你可能会避免错误的最近边。 在opencv中,有这个pointPolygonTest函数。 【参考方案1】:

对于闭合、不相交和方向良好的多边形,您可以通过将工作限制为基于this paper 的特征挤压来加快有符号距离场的计算。

如您在图像中所示(区域 A 和 E),离线最近的点位于边缘拉伸内。 B、C、D中的点最接近的特征不是边而是顶点。

算法是:

为每个边和顶点构造负和正挤压 对于每个点,确定它们所在的突出部分并找到正确符号的最小幅度距离。

这项工作被简化为多边形中的点测试和到线和点的距离计算。为了减少工作量,您可以考虑使用相同尺寸的有限拉伸来定义计算距离值的薄壳。这似乎是您的光晕着色示例所需的功能。

虽然您仍然需要迭代所有特征,但两种挤压类型始终是凸的,因此您可以提前退出四叉树、半平面测试和其他优化,尤其是在挤压距离有限的情况下。

边缘的拉伸是表面法线方向上的矩形(内部距离为负法线)。

来自1:

顶点的挤压是楔形,其边是在该顶点相交的边的法线。根据边之间的角度,顶点具有正或负拉伸。由于空间被边缘拉伸覆盖,平面顶点将没有拉伸。

来自1:

【讨论】:

【参考方案2】:

我希望这能解决你的问题。

这是在 Python 中实现的。

首先,我们使用 imageio 将图像作为数组导入。我们需要使用您的图像的修改版本(我将内部区域填充为白色)。

然后,我们将 RGBA 矩阵转换为具有 0 轮廓的二进制矩阵来定义您的界面(代码 sn-p 中的 phi)

这是来自下面 sn-p 的 phi(内部区域值 = +0.5,外部区域值 = -0.5):

import imageio
import numpy as np
import matplotlib.pyplot as plt
import skfmm

# Load image
im = imageio.imread("0WmVI_filled.png")
ima = np.array(im)

# Differentiate the inside / outside region
phi = np.int64(np.any(ima[:, :, :3], axis = 2))
# The array will go from - 1 to 0. Add 0.5(arbitrary) so there 's a 0 contour.
phi = np.where(phi, 0, -1) + 0.5

# Show phi
plt.imshow(phi)
plt.xticks([])
plt.yticks([])
plt.colorbar()
plt.show()

# Compute signed distance
# dx = cell(pixel) size
sd = skfmm.distance(phi, dx = 1)

# Plot results
plt.imshow(sd)
plt.colorbar()
plt.show()

最后,我们使用 scikit-fmm 模块来计算有符号距离。

这是生成的有符号距离场:

【讨论】:

谢谢罗宾。它并没有真正解决我的问题:我试图代表的地板很大(1 平方公里的工业综合体,具有相当详细的分辨率),所以我没有能力渲染它的全尺寸并对其进行一些行进算法。它将根据需要进行平铺和渲染。你的方法很有趣。再次感谢您的帮助。

以上是关于快速 2D 有符号距离的主要内容,如果未能解决你的问题,请参考以下文章

从不规则间隔点进行快速 2D 插值的实现和策略

UWP简单示例:快速开发2D游戏引擎

快速汉明距离评分

如何快速测量CAD图纸中的距离面积

生成包含多个 2D 点的矩形的快速算法

有人有 ERD 符号快速参考吗? [关闭]