用凹域域对一组点进行三角测量

时间:2017-07-20 23:59:36

标签: algorithm triangulation convex-hull delaunay

设置

给定凸包内的一些节点集,假设该域包含一个或多个凹面区域:

point distribution with concave areas

其中蓝点是点,黑线表示域。假设这些点保持为长度为points的2D数组n,其中n是点对的数量。

让我们使用scipy.spatial中的Delaunay方法对点进行三角测量:

triangulation within domain

如您所见,人们可能会遇到穿过域的三角形的创建。

问题

删除跨越域外的任何三角形有什么好的算法方法?理想但不一定,单面边缘仍保留域形状(即没有去除三角形的主要间隙)。

由于我的问题似乎仍在继续进行,我想跟进我目前使用的应用程序。

假设您已定义边界,可以使用ray casting algorithm来确定多边形是否在域内。

要做到这一点:

  1. 将每个多边形的质心视为C_i = (x_i,y_i)
  2. 然后,设想一条线L = [C_i,(+inf,y_i)]:即一条横跨您的域末端的一条线。
  3. 对于边界s_i中的每个边界线段S,检查与L的交叉点。如果是,请将+1添加到内部计数器intersection_count;否则,什么都不添加。
  4. 计算Ls_i for i=1..N之间所有交叉点的计数后:

    if intersection_count % 2 == 0:
        return True # triangle outside convex hull
    else:
        return False # triangle inside convex hull
    
  5. 如果没有明确定义您的边界,我发现它有助于映射'将形状放到布尔数组上并使用neighbor tracing algorithm来定义它。请注意,此方法假定一个实体域,您需要对具有“漏洞”的域使用更复杂的算法。在他们中间。

6 个答案:

答案 0 :(得分:2)

这里有一些Python代码可以满足您的需求。

首先,构建Alpha形状(请参见my previous answer

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 a line 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

要计算alpha形状的外边界的边缘,请使用以下示例调用:

edges = alpha_shape(points, alpha=alpha_value, only_outer=True)

现在,在计算edges的Alpha形状的外部边界的points之后,以下函数将确定点(x,y)是否在外部边界内。

def is_inside(x, y, points, edges, eps=1.0e-10):
    intersection_counter = 0
    for i, j in edges:
        assert abs((points[i,1]-y)*(points[j,1]-y)) > eps, 'Need to handle these end cases separately'
        y_in_edge_domain = ((points[i,1]-y)*(points[j,1]-y) < 0)
        if y_in_edge_domain:
            upper_ind, lower_ind = (i,j) if (points[i,1]-y) > 0 else (j,i)
            upper_x = points[upper_ind, 0] 
            upper_y = points[upper_ind, 1]
            lower_x = points[lower_ind, 0] 
            lower_y = points[lower_ind, 1]

            # is_left_turn predicate is evaluated with: sign(cross_product(upper-lower, p-lower))
            cross_prod = (upper_x - lower_x)*(y-lower_y) - (upper_y - lower_y)*(x-lower_x)
            assert abs(cross_prod) > eps, 'Need to handle these end cases separately'
            point_is_left_of_segment = (cross_prod > 0.0)
            if point_is_left_of_segment:
                intersection_counter = intersection_counter + 1
    return (intersection_counter % 2) != 0

enter image description here

在上图所示的输入中(取自我之前的answer),呼叫is_inside(1.5, 0.0, points, edges)将返回True,而is_inside(1.5, 3.0, points, edges)将返回False

请注意,上面的is_inside函数无法处理退化的情况。我添加了两个断言来检测这种情况(您可以定义适合您的应用程序的任何epsilon值)。在许多应用程序中,这已经足够了,但是如果没有,并且遇到这些最终情况,则需要将它们分开处理。 有关实现几何算法时的鲁棒性和精度问题,请参见here

答案 1 :(得分:1)

经典DT算法之一首先生成一个边界三角形,然后添加按x排序的所有新三角形,然后修剪出在超三角形中具有顶点的所有三角形。

至少从所提供的图像中,可以推导出修剪掉在凹形船体上具有所有顶点的一些三角形的启发式。如果没有证据,当要修剪的三角形的顶点按照与定义凹面船体相同的顺序排序时,它们会有一个负面区域。

这可能需要插入凹形船体,并进行修剪。

答案 2 :(得分:0)

使用this algorithm计算三角形质心,检查它是否在多边形内。

答案 3 :(得分:0)

您可以尝试约束 delaunay算法,例如使用sloan algoritm或cgal库。

[1] A Brute-Force Constrained Delaunay Triangulation?

答案 4 :(得分:0)

由于我的问题似乎仍在继续进行,我想跟进我目前使用的应用程序。

假设您已定义边界,可以使用ray casting algorithm来确定多边形是否在域内。

要做到这一点:

  1. 将每个多边形的质心视为C_i = (x_i,y_i)
  2. 然后,设想一条线L = [C_i,(+inf,y_i)]:即一条横跨您的域末端的一条线。
  3. 对于边界s_i中的每个边界线段S,检查与L的交叉点。如果是,请将+1添加到内部计数器intersection_count;否则,什么都不添加。
  4. 计算Ls_i for i=1..N之间所有交叉点的计数后:

    if intersection_count % 2 == 0:
        return True # triangle outside convex hull
    else:
        return False # triangle inside convex hull
    
  5. 如果没有明确定义您的边界,我发现它有助于映射&#39;将形状放到布尔数组上并使用neighbor tracing algorithm来定义它。请注意,此方法假定一个实体域,您需要对具有“漏洞”的域使用更复杂的算法。在他们中间。

答案 5 :(得分:0)

一种简单而优雅的方法是遍历三角形并检查它们是否在我们的domain内。 shapely软件包可以为您解决问题。

有关此内容的更多信息,请查看以下帖子:https://gis.stackexchange.com/a/352442 请注意,即使对于MultiPoin对象,也可以实现形状上的三角剖分。

我用了它,性能非常好,代码只有五行。