如何找到可以放入凹多边形内的最大圆?
只要能够实时处理具有~50个顶点的多边形,就可以使用强力算法。
答案 0 :(得分:40)
解决这个问题的关键是首先进行观察:适合任意多边形的最大圆的中心是:
为什么呢?因为圆的边缘上的每个点都与该中心等距。根据定义,最大的圆将具有最大的半径,并且将在至少两个点上触摸多边形,因此如果您发现距离多边形最远的点,则您找到了圆的中心。
此问题出现在地理位置,并以迭代方式解决为任意精度。它被称为无法接近的极点问题。请参阅Poles of Inaccessibility: A Calculation Algorithm for the Remotest Places on Earth。
基本算法的工作原理如下:
一个注意事项,如何测试一个点是否在多边形内部:问题的这一部分的最简单的解决方案是将光线投射到该点的右侧。如果它穿过奇数个边,则它在多边形内。如果它是偶数,它就在外面。
此外,至于测试到任何边缘的距离,有两种情况需要考虑:
(2)很容易。到边缘的距离是到两个顶点的距离的最小值。对于(1),该边缘上的最近点将是从您正在测试的点开始以90度角与边缘相交的点。请参阅Distance of a Point to a Ray or Segment。
答案 1 :(得分:13)
O(n log(n))算法:
答案 2 :(得分:13)
如果有人正在寻找实际的实现,我设计了一个更快的算法,可以在给定的精度下解决这个问题并使其成为一个JavaScript库。它类似于@cletus描述的迭代网格算法,但它保证获得全局最优,并且在实践中也快20-40倍。
检查出来:https://github.com/mapbox/polylabel
答案 3 :(得分:6)
总结:理论上,这可以在O(n)时间内完成。实际上,你可以在O(n log n)时间内完成。
广义Voronoi图。
如果您将多边形的顶点和边缘视为一组网站并将内部细分为“最近邻居单元格”,那么您将获得所谓的(通用)Voronoi图。 Voronoi图由连接它们的节点和边组成。节点的间隙是指定义多边形面的距离。
(这里多边形甚至有孔;原理仍然有效。)
现在的关键观察是最大内切圆的中心接触多边形的三个面(顶点或边),并且没有其他面可以更接近。因此,中心必须位于Voronoi节点上,即具有最大间隙的节点。
在上面的示例中,标记最大内切圆的中心的节点接触多边形的两个边和顶点。
顺便说一下,内侧轴是Voronoi图,其中去除了从反射顶点发出的Voronoi边缘。因此,最大内切圆的中心也位于中轴上。
来源:我的blog article,用于处理某些点上最大内切圆的概括。在那里你可以找到更多关于Voronoi图及其与最大内切圆的关系。
算法&实现强>
您实际上可以计算Voronoi图。 Fortune给出了点和段的最坏情况O(n log n)算法,用于Voronoi图的扫描线算法,SoCG'86。 Held发布了具有预期的O(n log n)时间复杂度的软件包Vroni,其实际上也计算了最大内切圆。而boost似乎也有一个实现。
对于简单的多边形(即无孔),在O(n)时间内运行的时间最优算法是由于Chin等人,Finding the Medial Axis of a Simple Polygon in Linear Time,1999。
蛮力。
然而,正如你所说的那样你对蛮力算法很好:如何简单地尝试所有三维网站(顶点和边缘)。对于每个三元组,您可以找到候选Voronoi节点,即三个站点的等距轨迹,并检查是否有任何其他站点与候选最大内切圆相交。如果有交叉路口,则解雇候选人。你可以找到所有三胞胎中最好的。
请参阅我Master thesis中的第3章,了解有关计算三个站点的等距基因座的更多详细信息。
答案 4 :(得分:1)
O(n log X)算法,其中X取决于您想要的精度。
二进制搜索圆的最大半径R:
在每次迭代中,对于给定的半径r,将每个边E向内推“R”,得到E'。对于每个边E',将半平面H定义为多边形“内部”的所有点的集合(使用E'作为边界)。现在,计算所有这些半平面E'的交集,这可以在O(n)时间内完成。如果交点非空,则如果使用交点中的任意点作为中心绘制半径为r的圆,则它将位于给定多边形内。
答案 5 :(得分:0)
我使用“直形骨骼”通过三个步骤将图像放置在多边形内:
在以下位置尝试:https://smart-diagram.com/diagram-designer/?template=365
答案 6 :(得分:0)
我实现了一个基于cv2的python代码,以获取mask / polygon / contours中最大/最大的内接圆。它支持非凸/空心形状。
import cv2
import numpy as np
def get_test_mask():
# Create an image
r = 100
mask = np.zeros((4 * r, 4 * r), dtype=np.uint8)
# Create a sequence of points to make a contour
vert = [None] * 6
vert[0] = (3 * r // 2, int(1.34 * r))
vert[1] = (1 * r, 2 * r)
vert[2] = (3 * r // 2, int(2.866 * r))
vert[3] = (5 * r // 2, int(2.866 * r))
vert[4] = (3 * r, 2 * r)
vert[5] = (5 * r // 2, int(1.34 * r))
# Draw it in mask
for i in range(6):
cv2.line(mask, vert[i], vert[(i + 1) % 6], (255), 63)
return mask
mask = get_test_mask()
"""
Get the maximum/largest inscribed circle inside mask/polygon/contours.
Support non-convex/hollow shape
"""
dist_map = cv2.distanceTransform(mask, cv2.DIST_L2, cv2.DIST_MASK_PRECISE)
_, radius, _, center = cv2.minMaxLoc(dist_map)
result = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)
cv2.circle(result, tuple(center), int(radius), (0, 0, 255), 2, cv2.LINE_8, 0)
# minEnclosingCircle directly by cv2
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
center2, radius2 = cv2.minEnclosingCircle(np.concatenate(contours, 0))
cv2.circle(result, (int(center2[0]), int(center2[1])), int(radius2), (0, 255, 0,), 2)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
来源:https://gist.github.com/DIYer22/f82dc329b27c2766b21bec4a563703cc