包含一组给定点的边界

时间:2018-05-27 04:56:32

标签: geometry computational-geometry concave-hull

我正在使用的算法遇到一些问题。我想让它成为一个边界。

以下是当前行为的示例:

Current behavior

以下是需要行为的MSPaint示例:

Wanted behavior

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

所以这是我的问题:

1)这甚至可能吗?

R:是的

2)这甚至被称为Convex Hull? (我不这么认为)

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

3)与传统的凸包相比,它的性能是否更低?

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

4)这个算法在伪代码或类似的东西中的例子?

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

4 个答案:

答案 0 :(得分:5)

这是一些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

如果您使用以下测试代码运行它,您将获得此图,看起来像您需要的: enter image description here

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()

编辑:在评论中发出请求后,这里有一些代码&#34;缝合&#34;输出边缘设置为连续边缘序列。

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

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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

它可以在Delaunay三角剖分中建立,时间为O(N log N)。

答案 3 :(得分:0)

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