【问题标题】:Boundary enclosing a given set of points包围给定点集的边界
【发布时间】:2018-11-06 00:42:41
【问题描述】:

我目前使用的算法有点问题。我想让它做一个边界。

以下是当前行为的示例:

这是一个 MSPaint 想要的行为示例:

C#中Convex Hull的当前代码:https://hastebin.com/dudejesuja.cs

所以这是我的问题:

1) 这可能吗?

R:是的

2) 这甚至被称为凸壳吗? (我不这么认为)

R:不叫边界,链接:https://www.mathworks.com/help/matlab/ref/boundary.html

3) 这会比传统的凸包对性能更友好吗?

R:据我研究,它应该是相同的性能

4) 该算法的伪代码或类似代码示例?

R:还没有回答或者我还没有找到解决方案

【问题讨论】:

  • 1.事实上,这不是一个凸包。凸包将有 no 凸性,即没有塌陷的部分。它只是外部的四个点。 2. 我不明白你真正想要什么。为什么只有中间的那两个点是你选择的?为什么没有其他任何点?在您的实际用例中,这可能对您更有意义,但就本示例中的连接点而言,您的选择似乎完全是任意的。为什么使用 6 点而不是像左边的 5 点?为什么不像真正的凸包那样 4 点呢?
  • @AlexanderReynolds 是的,对不起,我画错了,这是一个不好的例子,我重新做了这个例子,我认为这个更好,你可以想象无数(不是无穷大),在我所做的线条内,我希望它在正确的图像上呈现曲线。
  • 为什么你的画在右边不是这个:imgur.com/a/Ot9W9d9?当然,您可以继续将相同的想法应用于其他点。您实际上是从这个过程中的点以外的任何东西开始的,还是真的是您拥有这些点的唯一信息?只是想确保这不是XY problem。如果我是你,我会尝试在每个顶点之间绘制连接(制作完整的图表)。然后开始删除你不想要的外线,看看是否遵循你可以使用的规则。
  • 看起来你可能想要一个轮廓,但这很难在这样的离散点上定义。然而,任何产生这些点的规则都可能有助于制作轮廓。这些点在图像中来自哪里?再次澄清一下——我知道这不是你想要的——但你的标题和问题(最小凸包)你在第一张图片中得到的,所以你想要什么不是凸包。
  • 寻找alpha shapes

标签: geometry computational-geometry concave-hull


【解决方案1】:

这是一些 Python 代码,用于计算 alpha 形状(凹壳)并仅保留外边界。这大概就是matlab的边界在里面做的吧。

from scipy.spatial import Delaunay
import numpy as np


def alpha_shape(points, alpha, only_outer=True):
    """
    Compute the alpha shape (concave hull) of a set of points.
    :param points: np.array of shape (n,2) points.
    :param alpha: alpha value.
    :param only_outer: boolean value to specify if we keep only the outer border
    or also inner edges.
    :return: set of (i,j) pairs representing edges of the alpha-shape. (i,j) are
    the indices in the points array.
    """
    assert points.shape[0] > 3, "Need at least four points"

    def add_edge(edges, i, j):
        """
        Add an edge between the i-th and j-th points,
        if not in the list already
        """
        if (i, j) in edges or (j, i) in edges:
            # already added
            assert (j, i) in edges, "Can't go twice over same directed edge right?"
            if only_outer:
                # if both neighboring triangles are in shape, it's not a boundary edge
                edges.remove((j, i))
            return
        edges.add((i, j))

    tri = Delaunay(points)
    edges = set()
    # Loop over triangles:
    # ia, ib, ic = indices of corner points of the triangle
    for ia, ib, ic in tri.vertices:
        pa = points[ia]
        pb = points[ib]
        pc = points[ic]
        # Computing radius of triangle circumcircle
        # www.mathalino.com/reviewer/derivation-of-formulas/derivation-of-formula-for-radius-of-circumcircle
        a = np.sqrt((pa[0] - pb[0]) ** 2 + (pa[1] - pb[1]) ** 2)
        b = np.sqrt((pb[0] - pc[0]) ** 2 + (pb[1] - pc[1]) ** 2)
        c = np.sqrt((pc[0] - pa[0]) ** 2 + (pc[1] - pa[1]) ** 2)
        s = (a + b + c) / 2.0
        area = np.sqrt(s * (s - a) * (s - b) * (s - c))
        circum_r = a * b * c / (4.0 * area)
        if circum_r < alpha:
            add_edge(edges, ia, ib)
            add_edge(edges, ib, ic)
            add_edge(edges, ic, ia)
    return edges

如果你用下面的测试代码运行它,你会得到这个图,它看起来像你需要的:

from matplotlib.pyplot import *

# Constructing the input point data
np.random.seed(0)
x = 3.0 * np.random.rand(2000)
y = 2.0 * np.random.rand(2000) - 1.0
inside = ((x ** 2 + y ** 2 > 1.0) & ((x - 3) ** 2 + y ** 2 > 1.0)
points = np.vstack([x[inside], y[inside]]).T

# Computing the alpha shape
edges = alpha_shape(points, alpha=0.25, only_outer=True)

# Plotting the output
figure()
axis('equal')
plot(points[:, 0], points[:, 1], '.')
for i, j in edges:
    plot(points[[i, j], 0], points[[i, j], 1])
show()

编辑:根据评论中的请求,这里有一些代码将输出边集“缝合”成连续边的序列。

def find_edges_with(i, edge_set):
    i_first = [j for (x,j) in edge_set if x==i]
    i_second = [j for (j,x) in edge_set if x==i]
    return i_first,i_second

def stitch_boundaries(edges):
    edge_set = edges.copy()
    boundary_lst = []
    while len(edge_set) > 0:
        boundary = []
        edge0 = edge_set.pop()
        boundary.append(edge0)
        last_edge = edge0
        while len(edge_set) > 0:
            i,j = last_edge
            j_first, j_second = find_edges_with(j, edge_set)
            if j_first:
                edge_set.remove((j, j_first[0]))
                edge_with_j = (j, j_first[0])
                boundary.append(edge_with_j)
                last_edge = edge_with_j
            elif j_second:
                edge_set.remove((j_second[0], j))
                edge_with_j = (j, j_second[0])  # flip edge rep
                boundary.append(edge_with_j)
                last_edge = edge_with_j

            if edge0[0] == last_edge[1]:
                break

        boundary_lst.append(boundary)
    return boundary_lst

然后您可以遍历边界列表的列表,并在每条边中附加与第一个索引对应的点以获得边界多边形。

【讨论】:

  • 很高兴有一些代码作为答案。请注意,随着 edges 的添加,这将变得越来越慢。
  • 我不确定,因为 edgesset 并且一条边最多被访问两次,我认为运行时将由构造Delaunay三角剖分。绘图代码虽然效率不高..
  • 有没有人尝试将输出轮廓线转换为 shapefile 多边形;可能槽形匀称?到目前为止,我一直在尝试不成功。感谢有关这方面的任何指导。
  • 我编辑了答案以添加将边缘集缝合到边界多边形的代码。一般的想法是将连续的边附加到链的末端,直到它关闭多边形。希望这会有所帮助。
  • 我相信您使用的 alpha 值可能太小了。你的点比较远,所以我尝试了 alpha=1000 和 alpha=2500 的值并得到了预期的结果。
【解决方案2】:

这是构建凹壳的 JavaScript 代码:https://github.com/AndriiHeonia/hull 可能你可以将它移植到 C#。

【讨论】:

    【解决方案3】:

    考虑使用 Alpha 形状,有时称为凹壳。 https://en.wikipedia.org/wiki/Alpha_shape

    它可以从 Delaunay 三角剖分构建,时间为 O(N log N)。

    【讨论】:

      【解决方案4】:

      我会使用不同的方法来解决这个问题。由于我们使用的是一组二维点,因此计算点区域的边界矩形很简单。然后我将这个矩形按水平线和垂直线划分为“单元”,并为每个单元简单地计算位于其边界内的像素数。由于每个单元格只能有 4 个相邻单元格(单元格边相邻),因此边界单元格将是具有至少一个空相邻单元格或单元格边位于边界矩形边界的单元格。然后边界将沿着边界单元边构建。边界看起来像一个“楼梯”,但选择较小的像元大小会改善结果。事实上,细胞大小应通过实验确定;它不能太小,否则区域内可能会出现空单元格。点之间的平均距离可以作为像元大小的下界。

      【讨论】:

      • 您所描述的称为四叉树
      猜你喜欢
      • 2013-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-13
      • 1970-01-01
      • 1970-01-01
      • 2012-02-22
      • 2019-10-03
      相关资源
      最近更新 更多