Граница, охватывающая данный набор точек - PullRequest
0 голосов
/ 27 мая 2018

У меня небольшая проблема с алгоритмом, который я сейчас использую.Я хотел, чтобы это сделало границу.

Вот пример текущего поведения:

Current behavior

Вот пример MSPaint требуемого поведения:

Wanted behavior

Текущий код выпуклой оболочки в C #: https://hastebin.com/dudejesuja.cs

Итак, вот мои вопросы:

1) Это дажевозможно?

R: Да

2) Это даже называется выпуклой оболочкой?(Я так не думаю)

R: Нет, это называется границей, ссылка: https://www.mathworks.com/help/matlab/ref/boundary.html

3) Будет ли это менее дружественным к производительности, чем обычный выпуклый корпус?

R: Ну, насколько я исследовал, должна быть та же производительность

4) Пример этого алгоритма в псевдокоде или что-то подобное?

R: Еще не ответил или я не ответилпока не нашли решение

Ответы [ 4 ]

0 голосов
/ 28 июня 2018

Вот код JavaScript, который создает вогнутый корпус: https://github.com/AndriiHeonia/hull Возможно, вы можете перенести его на C #.

0 голосов
/ 29 мая 2018

Рассмотрите возможность использования альфа-формы, иногда называемой вогнутой оболочкой.https://en.wikipedia.org/wiki/Alpha_shape

Он может быть построен из триангуляции Делоне за время O (N log N).

0 голосов
/ 06 июня 2018

Вот некоторый код Python, который вычисляет альфа-форму (вогнутый корпус) и сохраняет только внешнюю границу.Это, вероятно, то, что делает граница 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()

РЕДАКТИРОВАТЬ: После запроса в комментарии, вот некоторый код, который "сшивает" выходной край, установленный в последовательности последовательных краев.

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

Вы можете затем перейти черезсписок граничных списков и добавьте точки, соответствующие первому индексу в каждом ребре, чтобы получить граничный многоугольник.

0 голосов
/ 29 мая 2018

Я бы использовал другой подход для решения этой проблемы.Поскольку мы работаем с двумерным набором точек, вычислить ограничивающий прямоугольник области точек несложно.Затем я разделил бы этот прямоугольник на «ячейки» горизонтальными и вертикальными линиями, и для каждой ячейки просто посчитал количество пикселей, находящихся в ее пределах.Поскольку каждая ячейка может иметь только 4 смежные ячейки (смежные по сторонам ячейки), то граничными ячейками будут те, которые имеют по меньшей мере одну пустую соседнюю ячейку или имеют сторону ячейки, расположенную на границе ограничивающего прямоугольника.Тогда граница будет построена вдоль граничных сторон ячейки.Граница будет выглядеть как «лестница», но выбор меньшего размера ячейки улучшит результат.На самом деле, размер ячейки должен быть определен экспериментально;он не может быть слишком маленьким, иначе внутри области могут появиться пустые ячейки.Среднее расстояние между точками может использоваться в качестве нижней границы размера ячейки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...