是否有有效的算法来生成 2D 凹壳?

Posted

技术标签:

【中文标题】是否有有效的算法来生成 2D 凹壳?【英文标题】:Is there an efficient algorithm to generate a 2D concave hull? 【发布时间】:2010-09-10 03:52:12 【问题描述】:

拥有来自 GIS 文件(城市地图)的一组(2D)点,我需要生成定义该地图(其边界)的“轮廓”的多边形。它的输入参数将是点集和“最大边长”。然后它将输出相应的(可能是非凸的)多边形。

到目前为止,我发现的最佳解决方案是生成 Delaunay 三角形,然后移除比最大边长更长的外部边。在所有外部边缘都比这短之后,我只需删除内部边缘并获得我想要的多边形。问题是,这非常耗时,我想知道是否有更好的方法。

【问题讨论】:

实际上,我认为 ArcGIS 没有内置算法来做他想做的事。 ArcGIS 有能力做凸包,但凹包要复杂得多。 您能更准确地定义您的问题吗? :) 5 分:正方形的 4 个角及其中心。你的边界是什么?如果您的最大边长允许中心,那么您将“弯曲”到正方形的 4 条边中的哪条边以包括中间点是完全任意的。 是的,我做到了,我在问题中提到的方式(使用德劳内三角形)。后来我对下面 nsanders 指出的 alpha 形状概念做了一些工作,但在我成功之前,问题的优先级降低了,我转向另一个问题。 Delaunay 具有正确的复杂度 (O(n log n))。我怀疑你可以渐近地做得更好。 @frank 这不是手动的,它是为 GPS 导航应用程序处理地图的自动化工具的一部分。在我的具体情况下,这确实是任意的——这些点是街角,而生成的多边形将是一个城市的轮廓。我使用了一个任意值,它会给我一个足够详细的多边形,不会太重而无法渲染。我认为必须这样,您必须根据应用程序的需要确定最大长度 - 我不知道您如何事先自动计算它。 【参考方案1】:

Bing Maps V8 交互式 SDK 在高级形状操作中具有凹壳选项。

https://www.bing.com/mapspreview/sdkrelease/mapcontrol/isdk/advancedshapeoperations?toWww=1&redig=D53FACBB1A00423195C53D841EA0D14E#JS

在 ArcGIS 10.5.1 中,3D Analyst 扩展模块具有最小边界体积工具,其几何类型包括凹壳、球体、包络或凸壳。它可以在任何许可级别上使用。

这里有一个凹壳算法:https://github.com/mapbox/concaveman

【讨论】:

【参考方案2】:

作为广泛采用的参考,PostGIS 从凸包开始,然后将其嵌入,您可以在此处看到它。

https://github.com/postgis/postgis/blob/380583da73227ca1a52da0e0b3413b92ae69af9d/postgis/postgis.sql.in#L5819

【讨论】:

【参考方案3】:

你可以使用这个插件在 QGIS 中完成; https://github.com/detlevn/QGIS-ConcaveHull-Plugin

根据您需要它与数据交互的方式,可能值得在这里查看它是如何完成的。

【讨论】:

【参考方案4】:

This 论文讨论了高效生成简单多边形以表征平面中一组点的形状,并提供了算法。还有一个使用相同算法的 Java 小程序 here。

【讨论】:

链接失效。 Java 源代码似乎在 ambientspatial.net/ddo/?p=143【参考方案5】:

好问题!我根本没有尝试过,但我的第一个尝试是这种迭代方法:

    创建一个集合 N(“不包含”),并将集合中的所有点添加到 N。 从 N 中随机选取 3 个点组成一个初始多边形 P。将它们从 N 中移除。 使用some point-in-polygon algorithm 并查看 N 中的点。对于 N 中的每个点,如果它现在被 P 包含,则将其从 N 中删除。一旦在 N 中找到仍然不包含在 P 中的点,继续第 4 步。如果 N 为空,则完成。 调用你找到的点 A。在 P 中找到最靠近 A 的线,并在其中间添加 A。 返回步骤 3

我认为只要它表现得足够好,它就会起作用——对你最初的 3 点进行良好的启发式可能会有所帮助。

祝你好运!

【讨论】:

【参考方案6】:

对于其他人来说,答案可能仍然很有趣:可以应用 行进方阵算法的变体,应用 (1) 在凹壳内,然后 (2) 然后继续(例如 3)不同的尺度,我取决于点的平均密度。比例尺必须是彼此的整数倍,这样您就可以构建一个可用于有效采样的网格。这允许快速找到空样本=正方形、完全在点的“集群/云”内的样本,以及介于两者之间的样本。然后可以使用后一类轻松确定代表凹壳一部分的折线。

在这种方法中一切都是线性的,不需要三角测量,它不使用 alpha 形状,并且不同于此处描述的商业/专利产品 (http://www.concavehull.com/)

【讨论】:

【参考方案7】:

一个简单的解决方案是绕着多边形的边缘走。给定连接点 P0 和 P1 的边界的当前边,边界 P2 上的下一个点将是 A 可能最小的点,其中

H01 = bearing from P0 to P1
H12 = bearing from P1 to P2
A = fmod( H12-H01+360, 360 )
|P2-P1| <= MaxEdgeLength

然后你设置

P0 <- P1
P1 <- P2

并重复,直到你回到你开始的地方。

这仍然是 O(N^2),所以你需要对你的点列表进行一点排序。如果您根据城市中心的方位对点进行排序,则可以限制每次迭代时需要考虑的点集。

【讨论】:

【参考方案8】:

here 的家伙声称已经开发了一种 k 最近邻方法来确定一组点的凹壳,其行为“在点的数量上几乎呈线性”。遗憾的是,他们的论文似乎受到了很好的保护,您必须向 them 询问。

这是一个good set of references,其中包含上述内容,可能会引导您找到更好的方法。

【讨论】:

看起来这是有问题的论文:repositorium.sdum.uminho.pt/bitstream/1822/6429/1/… 这个想法非常聪明和简单——据我所知,它只是对凸空值的格雷厄姆扫描技术的直接修改.无需寻找 Delaunay 三角剖分等。 说论文名称是“凹壳:计算一组点所占据区域的k-最近邻方法”在这里可以找到:repositorium.sdum.uminho.pt/handle/1822/6429【参考方案9】:

一个快速的近似解决方案(也适用于凸包)是找到每个小元素东西向的南北边界。

根据您需要多少细节,创建一个固定大小的上限/下限数组。 对于每个点,计算它所在的 E-W 列,然后更新该列的上限/下限。处理完所有点后,您可以为那些错过的列插入上/下点。

对于非常长的薄形状,还值得事先快速检查一下,然后决定是分类为 NS 还是 Ew。

【讨论】:

【参考方案10】:

我们实验室的一位前学生在他的博士论文中使用了一些适用的技术。我相信其中之一被称为“alpha 形状”,并在以下论文中被引用:

http://www.cis.rit.edu/people/faculty/kerekes/pdfs/AIPR_2007_Gurram.pdf

该论文提供了一些您可以参考的进一步参考。

【讨论】:

alpha 形状基于 Delaunay 三角剖分,因此肯定会涉及一次 Delaunay 三角剖分计算 在我看来,alpha 形状只是一个概念。

以上是关于是否有有效的算法来生成 2D 凹壳?的主要内容,如果未能解决你的问题,请参考以下文章

将 OpenGL 用于 2d 游戏的最有效方法是啥?

如何计算 2D numpy 数组的所有列的总和(有效)

是否有更有效的匹配版本来搜索数字的重复排列?

swift算法:数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

生成类独有的id的有效方法?

数据结构与算法之深入解析“有效的正方形”的求解思路与算法示例