找到不规则形状多边形的“视觉”中心的最快方法是什么?

时间:2009-07-29 21:23:25

标签: polygon center point buffering

我需要找到一个点,它是一个不规则形状的多边形的视觉中心。通过视觉中心,我的意思是在视觉上看起来位于多边形的大区域的中心的点。应用程序是在多边形内放置一个标签。

这是一个使用内部缓冲的解决方案:

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

如果要使用它,找到缓冲区的有效且快速的方法是什么?如果要使用任何其他方式,那是哪种方式?

真正坚韧的多边形的一个很好的例子是一个巨大的厚U(用Arial Black或Impact或某些这样的字体书写)。

15 个答案:

答案 0 :(得分:11)

我从MapBox中找到了一个名为Polylabel的非常好的解决方案。完整的来源也可以在Github上找到。

基本上它试图找到多边形的视觉中心,如T Austin所说。

enter image description here

某些细节表明这可能是一个切实可行的解决方案:

  

不幸的是,计算[理想解决方案]既复杂又困难   而且很慢。已公布的问题解决方案也需要   受约束的Delaunay三角剖分或计算直骨架为   预处理步骤 - 这两个步骤都很慢且容易出错。

     

对于我们的用例,我们不需要一个确切的解决方案 - 我们愿意   交易一些精度以获得更快的速度。当我们贴上标签时   一张地图,以毫秒计算它比计算更重要   在数学上是完美的。

关于使用情况的快速说明。源代码适用于开箱即用的Javascript,但是如果您打算将其用于“普通”多边形,则应将其包装在空数组中,因为此处的函数采用GeoJSONPolygons而非正常多边形,即

var myPolygon = [[x1, y1], [x2, y2], [x3, y3]];
var center = polylabel([myPolygon]);

答案 1 :(得分:10)

如果您可以将多边形转换为二进制图像,则可以使用图像处理领域中存在的基础,例如:A Fast Skeleton Algorithm on Block Represented Binary Images

但由于离散化错误和额外工作,这在一般情况下并不合理。

但是,也许你觉得这些很有用:

编辑:也许您想要寻找多边形中包含的最大圆的中心点。它不一定总是在被观察的中心,但大部分时间可能会给出预期的结果,并且只在略微病态的情况下完全关闭。

答案 2 :(得分:7)

答案 3 :(得分:7)

怎么样:

如果多边形的质心在多边形内,则使用它,否则:

1)从质心通过多边形延伸一条线,将多边形分成两半相等的区域

2)“视觉中心”是在线接触周边的最近点和在远离质心的方向上切割周边的下一点之间的点

以下是一些图片来说明:

enter image description here

enter image description here

答案 4 :(得分:2)

计算多边形每条边的中心位置(x,y)。您可以通过查找每条边的末端位置之间的差异来实现此目的。取每个维度中每个中心的平均值。这将是多边形的中心。

答案 5 :(得分:2)

已经多次提出了质心方法。我认为这是一个很好的资源,非常直观地描述了这个过程(以及许多其他有用的多边形技巧):

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

此外,为了放置一个简单的UI标签,仅计算多边形的边界框(由多边形中任何顶点的最低和最高x和y坐标定义的矩形)并获得其中心可能就足够了于:

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

这比计算质心要快一些,这对于实时或嵌入式应用程序来说可能很重要。

另请注意,如果您的多边形是静态的(它们不会改变形状),您可以通过将BB中心/质心计算的结果(相对于例如多边形的第一个顶点)保存到多边形的数据结构。

答案 6 :(得分:2)

以下是我尝试过的四种不同方法。

  1. cv2 基于重心 (get_center_of_mass)
  2. shapely 基于代表点 (get_representative_point)
  3. cv2 + skimage.skeleton 基于 skeletonized 形状 (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 也很快,但是识别的点,虽然总是保证留在形状内(或者即使是多个不连接的形状,也可以进行微小的编辑!),与对象的中心没有太大关系< /li>
  • 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

答案 7 :(得分:1)

我并不是说这是最快的,但它会在多边形内部给你一个点。计算Straight Skeleton。你正在寻找的是这个骨架。例如,您可以选择距离边界框中心的最短法线距离的那个。

答案 8 :(得分:0)

如何找到多边形的“内圆”(适合其内部的最大圆圈),然后将标签置于其中心?以下是一些可以帮助您入门的链接:

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

这很不可能完美地适用于每个多边形;看起来像C的多边形会使标签位于某个不可预测的位置。但优点是标签总是与多边形的实体部分重叠。

答案 9 :(得分:-1)

我认为如果你将多边形折断回它的顶点,然后应用一个函数来找到最大的凸包,然后找到该凸包的中心,它将与“明显”中心紧密匹配。

在给定顶点的情况下找到最大的凸包:Look under the Simple Polygon paragraph.

平均凸包的顶点以找到中心。

答案 10 :(得分:-1)

如果我理解你所关注的论文的意思(非常有趣的问题,顺便说一句),这种“内部缓冲”技术有点类似于用一块糖溶解的一块糖来模拟所讨论的形状。 。中的边缘(例如缓冲距离增加,原始形状保留较少)剩下的最后一位是放置标签的理想位置。

遗憾的是,如何在算法中完成此操作并不是很清楚....

答案 11 :(得分:-1)

你可以将标签放在天然中心(可能是边界框),然后根据局部多边形边和标签BB的交点移动它吗?沿着交叉边的法线移动,如果多个边相交,则将它们的法线相加以进行移动?

在这里猜测;在这类问题中,我可能会尝试迭代地解决,只要性能不是太大的问题。

答案 12 :(得分:-1)

现在没有太多时间详细说明或测试,但是当我有机会时,我会尝试做更多的事情。

使用质心作为主要方法。测试质心是否在多边形内;如果没有,则在的最近点和多边形的另一边画一条线。在多边形内该线段的中点处,放置标签。

因为距离质心最近的点可能会限制相当大的区域,我认为这可能会产生类似于Kyralessa's incircles的结果。当然,如果你有一个带孔的多边形,这可能会发狂。在这种情况下,收视率可能会好得多。另一方面,对于典型情况,它默认为(快速?)质心方法。

答案 13 :(得分:-2)

这个问题可能类似于假设密度均匀的“质心”。

编辑:如果多边形有“洞”

,则此方法无效

答案 14 :(得分:-3)

你可以使用土木工程中使用的质心(或重心)方法,这里有一个来自维基百科的有用链接:

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