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

Вот решение, использующее внутреннюю буферизацию:

https://web.archive.org/web/20150708063910/http://proceedings.esri.com/library/userconf/proc01/professional/papers/pap388/p388.htm

Если это будет использоваться, каков эффективный и быстрый способ найти буфер? Если нужно использовать какой-либо другой способ, то какой?

Хорошим примером действительно сложных многоугольников является гигантская толстая буква U (написанная шрифтом Arial Black, Impact или другим подобным шрифтом).

56
Mikhil 30 Июл 2009 в 01:23

15 ответов

Лучший ответ

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

Но в общем случае это не совсем разумно из-за ошибок дискретизации и лишней работы.

Однако, возможно, они вам пригодятся:

РЕДАКТИРОВАТЬ: Возможно, вы хотите найти точку, которая является центром самого большого круга, содержащегося в многоугольнике. Это не обязательно всегда в наблюдаемом центре, но большую часть времени, вероятно, дало бы ожидаемый результат, и только в слегка патологических случаях что-то совсем не так.

13
Trung0246 10 Ноя 2017 в 06:43

Я нашел очень хорошее решение этой проблемы в MapBox под названием Polylabel. Полный исходный код также доступен на их Github.

По сути, он пытается найти визуальный центр многоугольника, как сказал Т. Остин.

enter image description here

Некоторые детали предполагают, что это может быть практическое решение:

К сожалению, вычисление [идеального решения] является сложным и медленным. Опубликованные решения проблемы требуют либо ограниченной триангуляции Делоне, либо вычисления прямого каркаса в качестве шагов предварительной обработки - оба эти действия медленны и подвержены ошибкам.

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

Небольшое примечание об использовании. Исходный код отлично работает с Javascript из коробки, однако, если вы намереваетесь использовать его с «нормальным» многоугольником, вам следует обернуть его в пустой массив, так как функции здесь принимают GeoJSONPolygons, а не обычные многоугольники, т.е.

var myPolygon = [[x1, y1], [x2, y2], [x3, y3]];
var center = polylabel([myPolygon]);
16
StayOnTarget 20 Фев 2018 в 05:17

Как насчет:

Если центроид многоугольника находится внутри многоугольника, используйте его, иначе:

1) Протяните линию от центроида через многоугольник, деля многоугольник на две половины равной площади.

2) «Визуальный центр» - это точка на полпути между ближайшей точкой, в которой линия касается периметра, и следующей точкой, пересекающей периметр в направлении от центроида.

Вот пара картинок, чтобы проиллюстрировать это:

enter image description here

enter image description here

8
Dan Lowe 2 Дек 2015 в 02:14

Вы изучали использование формулы центроида?

http://en.wikipedia.org/wiki/Centroid

http://en.wikipedia.org/wiki/K-means_algorithm

7
Arron S 29 Июл 2009 в 21:51

Метод центроида уже предлагался несколько раз. Я думаю, что это отличный ресурс, который очень интуитивно описывает процесс (и многие другие полезные трюки с полигонами):

http://paulbourke.net/geometry/polygonmesh/centroid.pdf

Кроме того, для размещения простой метки пользовательского интерфейса может быть достаточно просто вычислить ограничивающую рамку многоугольника (прямоугольник, определяемый наименьшими и наибольшими координатами x и y любой вершины многоугольника) и получить его центр в:

{
    x = min_x + (max_x - min_x)/2,
    y = min_y + (max_y - min_y)/2
}

Это немного быстрее, чем вычисление центроида, что может иметь значение для приложения реального времени или встроенного приложения.

Также обратите внимание, что если ваши многоугольники статичны (они не меняют форму), вы можете оптимизировать, сохранив результат вычисления центра / центра масс BB (относительно, например, первой вершины многоугольника) в структуре данных многоугольник.

2
AndyG 7 Фев 2014 в 17:18

Вот четыре разных подхода, которые я пробовал.

  1. Центр масс на основе cv2 (get_center_of_mass)
  2. Репрезентативная точка на основе shapely (get_representative_point)
  3. Центр масс cv2+ skimage.skeleton > скелетонизированная форма (get_skeleton_center_of_mass)
  4. scipy на основе наибольшего расстояния до границы (get_furthest_point_from_edge)
import numpy as np
import cv2
from shapely.geometry import Polygon
from skimage.morphology import skeletonize, medial_axis
from scipy.ndimage.morphology import distance_transform_edt
import matplotlib.pyplot as plt
H, W = 300, 300

def get_random_contour():
    xs = np.random.randint(0, W, 4)
    ys = np.random.randint(0, H, 4)
    cnt = np.array([[x,y] for x,y in zip(xs,ys)])
    mask = draw_contour_on_mask((H,W), cnt)
    cnt, _ = cv2.findContours(mask, 1, 2)
    cnt = cnt[0]
    return cnt

def draw_contour_on_mask(size, cnt):
    mask = np.zeros(size, dtype='uint8')
    mask = cv2.drawContours(mask, [cnt], -1, 255, -1)
    return mask

def get_center_of_mass(cnt):
    M = cv2.moments(cnt)
    cx = int(M['m10']/M['m00'])
    cy = int(M['m01']/M['m00'])
    return cx, cy

def get_representative_point(cnt):
    poly = Polygon(cnt.squeeze())
    cx = poly.representative_point().x
    cy = poly.representative_point().y
    return cx, cy

def get_skeleton_center_of_mass(cnt):
    mask = draw_contour_on_mask((H,W), cnt)
    skel = medial_axis(mask//255).astype(np.uint8) #<- medial_axis wants binary masks with value 0 and 1
    skel_cnt,_ = cv2.findContours(skel,1,2)
    skel_cnt = skel_cnt[0]
    M = cv2.moments(skel_cnt) 
    if(M["m00"]==0): # this is a line
        cx = int(np.mean(skel_cnt[...,0]))
        cy = int(np.mean(skel_cnt[...,1]))
    else:
        cx = int(M['m10']/M['m00'])
        cy = int(M['m01']/M['m00'])
    return cx, cy

def get_furthest_point_from_edge(cnt):
    mask = draw_contour_on_mask((H,W), cnt)
    d = distance_transform_edt(mask)
    cy, cx = np.unravel_index(d.argmax(), d.shape)
    return cx, cy

Вот мой анализ по теме:

  • get_center_of_mass является самым быстрым, но, как упоминалось в этом потоке, центр масс может быть расположен за пределами формы для невыпуклых форм.
  • get_representative_point также выполняется быстро, но идентифицированная точка, хотя всегда гарантированно остается внутри фигуры (или с небольшими правками, даже с несколькими несвязанными фигурами!), Не имеет ничего общего, если вообще имеет какое-либо отношение к центру объекта.
  • get_skeleton_center_of_mass возвращает приятную для восприятия центральную точку, но работает медленно и требует логики для отдельных фигур.
  • get_furthest_point_from_edge относительно быстро, легко обобщается на отдельные фигуры, а центральная точка визуально приятна
rows = 4
cols = 4
markers = ['x', '+', "*", "o"]
colors = ['r','b','g','orange']
functions = [get_center_of_mass, get_representative_point, get_skeleton_center_of_mass, get_furthest_point_from_edge]

plt.figure(figsize=(2*cols, 2*rows, ))
for i in range(rows*cols): 
    cnt = get_random_contour()
    mask = draw_contour_on_mask((H,W), cnt)
    
    plt.subplot(cols,rows, i+1)
    plt.imshow(mask, cmap='gray')
    for c, m, f in zip(colors, markers, functions):
        l = f.__name__
        cx, cy = f(cnt)
        plt.scatter(cx, cy, c=c, s=100, label=l, marker=m, alpha=0.7)

plt.tight_layout()    
plt.legend(loc=3)
plt.show()

enter image description here

Вот как алгоритмы, работающие на 100 случайных примерах, сравниваются по скорости:

N_EXAMPLES = 100
cnts = [get_random_contour() for _ in range(N_EXAMPLES)]

%%time
_ = [get_center_of_mass(cnt) for cnt in cnts]
CPU times: user 7.07 ms, sys: 155 µs, total: 7.23 ms
Wall time: 5.75 ms

%%time
_ = [get_representative_point(cnt) for cnt in cnts]
CPU times: user 23.6 ms, sys: 7.84 ms, total: 31.4 ms
Wall time: 28.5 ms

%%time
_ = [get_skeleton_center_of_mass(cnt) for cnt in cnts]
CPU times: user 5.56 s, sys: 3.31 ms, total: 5.56 s
Wall time: 5.55 s

%%time
_ = [get_furthest_point_from_edge(cnt) for cnt in cnts]
CPU times: user 486 ms, sys: 53 µs, total: 486 ms
Wall time: 485 ms
2
mjkvaak 10 Фев 2021 в 07:25

Я не говорю, что это самый быстрый, но он даст вам точку внутри многоугольника. Рассчитайте прямой скелет. Точка, которую вы ищете, находится на этом скелете. Например, вы можете выбрать тот, у которого самое короткое нормальное расстояние до центра ограничивающей рамки.

1
Harald Scheirich 29 Июл 2009 в 22:46

Как насчет того, чтобы найти «вписанную окружность» многоугольника (самый большой круг, который помещается внутри него), а затем центрировать метку в центре этого многоугольника? Вот пара ссылок, с которых можно начать:

http://www.mathopenref.com/polygonincircle.html
https://nrich.maths.org/discus/messages/145082/ 144373.html? 1219439473

Скорее всего, это не сработает идеально для каждого полигона; многоугольник, который выглядел бы как C, имел бы метку в несколько непредсказуемом месте. Но преимуществом было бы то, что метка всегда перекрывала твердую часть многоугольника.

0
Ryan Lundy 29 Июл 2009 в 21:58

Если я понимаю суть статьи, на которую вы ссылаетесь (довольно интересная проблема, кстати), эта техника «внутренней буферизации» в некоторой степени аналогична моделированию рассматриваемой формы из куска сахара, который растворяется кислотой с краев в . (например, по мере увеличения буферного расстояния остается меньше исходной формы) Последний оставшийся бит является идеальным местом для размещения метки.

Как добиться этого в алгоритме, к сожалению, мне не очень понятно ...

-1
Jason S 29 Июл 2009 в 21:34

Я думаю, что если вы разбили многоугольник обратно на вершины, а затем применили функцию, чтобы найти самую большую выпуклую оболочку, а затем найти центр вне этой выпуклой оболочки, он будет точно соответствовать «кажущемуся» центру.

Нахождение самой большой выпуклой оболочки с учетом вершин: Загляните под абзац "Простой многоугольник".

Усредните вершины выпуклой оболочки, чтобы найти центр.

-1
CookieOfFortune 29 Июл 2009 в 21:38

Не могли бы вы разместить метку в наивном центре (возможно, ограничивающего прямоугольника), а затем переместить ее на основе пересечения краев локального многоугольника и BB метки? Перемещаться по нормалям пересекающихся ребер, и если несколько ребер пересекаются, суммировать их нормали для движения?

Просто догадываюсь здесь; в такого рода проблемах я бы, вероятно, попытался решить итеративно, если производительность не слишком важна.

-1
dash-tom-bang 29 Июл 2009 в 21:49

Сейчас не так много времени, чтобы разрабатывать или тестировать это, но я постараюсь сделать больше, когда у меня будет возможность.

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

Поскольку точка, ближайшая к центроиду, вероятно, ограничит довольно большую площадь, я думаю, что это может дать результаты, аналогичные вписанным окружностям Киралессы. Конечно, это могло бы сойти с ума, если бы у вас был многоугольник с дырами. В этом случае, вероятно, было бы намного лучше вписаться в окружности. С другой стороны, для типичных случаев по умолчанию используется (быстрый?) Метод центроида.

-1
Matt Parker 29 Июл 2009 в 22:08

Эта проблема, вероятно, была бы аналогична поиску «центра масс» в предположении однородной плотности.

РЕДАКТИРОВАТЬ: этот метод не будет работать, если в многоугольнике есть «дыры».

-2
Janie 29 Июл 2009 в 21:35

Вы можете использовать метод центра масс (или центра тяжести), который используется в гражданском строительстве, вот полезная ссылка из википедии:

http://en.wikipedia.org/wiki/Center_of_mass

-3
likeitlikeit 25 Апр 2013 в 08:58

Вычислите положение центра (x, y) каждого края многоугольника. Вы можете сделать это, найдя разницу между положениями концов каждого края. Возьмите среднее значение каждого центра в каждом измерении. Это будет центр многоугольника.

2
ire_and_curses 29 Июл 2009 в 21:31