【问题标题】:Shrink polygon using corner coordinates使用角坐标收缩多边形
【发布时间】:2018-09-08 13:16:14
【问题描述】:

我试图弄清楚如何仅使用角坐标来缩小多边形。例如,如果我在[(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)] 有以下形状,那么形状看起来像这样:

如果我将这个多边形缩小一些宽度和高度因子,我想找到角坐标。例如,如果我想将其宽度缩小 10%,将高度缩小 20%,则可以显示如下:

我试图使用cv2.resize() 来做到这一点,但在调整大小后无法获得角落。我一直在尝试找到一种用于调整多边形大小或缩小多边形的算法,但找不到任何有关如何执行此操作的信息。是否存在用于执行此类操作的算法或包?

【问题讨论】:

  • 按照公式,使用简单的解析几何(编写直线方程,沿垂线偏移它们,确定新的交点)比使用关于视觉的 openCV 更容易解决这个问题/识别。
  • 在将对象点围绕原点居中后,对所有具有所需大小因子的点使用 perspectiveTransform。
  • 透视变换不会按照您希望的方式工作,但它与调整图像大小相同。

标签: python opencv


【解决方案1】:

我误读了这个问题,我放弃了答案,因为它可能对某人有所帮助,但我意识到最终输出不是想要的结果

要在收缩后获得多边形的新坐标,您可以将所有坐标 (position vectors) 与收缩因子相乘,如下所示:

x_shrink = 0.1
y_shrink = 0.2

coords = [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)]
xs = [i[0] for i in coords]
ys = [i[1] for i in coords]

# simplistic way of calculating a center of the graph, you can choose your own system
x_center = 0.5 * min(xs) + 0.5 * max(xs)
y_center = 0.5 * min(ys) + 0.5 * max(ys)

# shrink figure
new_xs = [(i - x_center) * (1 - x_shrink) + x_center for i in xs]
new_ys = [(i - y_center) * (1 - y_shrink) + y_center for i in ys]

# create list of new coordinates
new_coords = zip(new_xs, new_ys)

这会输出以下内容(蓝色是原始的,绿色是缩小的多边形)

【讨论】:

  • 这也是我最初想要做的。遗憾的是,它不起作用,因为缩小版本实际上会在多边形的凹入区域重叠,例如在您尝试的底部。
  • @greenthumbtack 如果您指定多个中心,一个用于左侧,一个用于右侧,一个用于中间,它可能会起作用。虽然它会变得非常大或大量手工......
  • 是的,但那将是另一个问题。多边形是由用户创建的,我无法事先确定它们的形状。我需要编写一种方法来确定我想象的许多不同的属性。
【解决方案2】:

我认为在数学上不可能在 x 中缩小百分比,在 y 中缩小百分比并且永远不会让布局移动到原始布局之外。然而,这只是一种预感。

此代码将所有线移动到靠近中心一定距离,然后找到所有线的新交点:

import matplotlib.pyplot as plt

def det(a, b):
        return a[0] * b[1] - a[1] * b[0]

def line_intersection(line1, line2):
    xdiff = (line1[0][0] - line1[1][0], line2[0][0] - line2[1][0])
    ydiff = (line1[0][1] - line1[1][1], line2[0][1] - line2[1][1]) #Typo was here

    div = det(xdiff, ydiff)
    if div == 0:
       raise Exception('lines do not intersect')

    d = (det(*line1), det(*line2))
    x = det(d, xdiff) / div
    y = det(d, ydiff) / div
    return x, y

# how much the coordinates are moved as an absolute value
shrink_value_x = 3
shrink_value_y = 1.5

# coords must be clockwise
coords = [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)]
lines = [[coords[i-1], coords[i]] for i in range(len(coords))]

new_lines = []
for i in lines:
    dx = i[1][0] - i[0][0]
    dy = i[1][1] - i[0][1]

    # this is to take into account slopes
    factor = 1 / (dx*dx + dy*dy)**0.5
    new_dx = dy*shrink_value_x * factor
    new_dy = dx*shrink_value_y * factor

    new_lines.append([(i[0][0] + new_dx, i[0][1] - new_dy),
                      (i[1][0] + new_dx, i[1][1] - new_dy)])

# find position of intersection of all the lines
new_coords = []
for i in range(len(new_lines)):
    new_coords.append((line_intersection(new_lines[i-1], new_lines[i])))

我从this answer@Paul Draper 获得了线交叉点代码。

这输出

【讨论】:

【解决方案3】:

据我了解,您正在搜索ST_Buffer from postgis 的功能,但具有不同的因素。 不幸的是,这并不容易实现(有关更多信息,请参阅one question in the qgis-stack)。

但是,如果使用相同的 x 和 y 因子(或作为更复杂算法的开始)已经有帮助,那么你可以这样做:

shapely 是一个使 ST_Buffer 函数可以在 python 中访问的库。

(如果您需要更多特定于地理数据的功能 geoalchemy2 可能是更好的选择。请注意这种情况下的 crs/srid 更改)


from shapely import geometry
import matplotlib.pyplot as plt

# your variables
coords = [(0, 0), (0, 100), (20, 100), (30, 60), (40, 100), (60, 100), (60, 0), (40, 10), (40, 40), (20, 40), (20, 10)]
lines = [[coords[i-1], coords[i]] for i in range(len(coords))]

# your factor of 10%
# Note: with 20% the polygon becomes a multi-polygon, so a loop for plotting would be needed.
factor = 0.1

# code from nathan
xs = [i[0] for i in coords]
ys = [i[1] for i in coords]
x_center = 0.5 * min(xs) + 0.5 * max(xs)
y_center = 0.5 * min(ys) + 0.5 * max(ys)

min_corner = geometry.Point(min(xs), min(ys))
max_corner = geometry.Point(max(xs), max(ys))
center = geometry.Point(x_center, y_center)
shrink_distance = center.distance(min_corner)*factor

assert abs(shrink_distance - center.distance(max_corner)) < 0.0001

my_polygon = geometry.Polygon(coords)
my_polygon_shrunken = my_polygon.buffer(-shrink_distance)


x, y = my_polygon.exterior.xy
plt.plot(x,y)
x, y = my_polygon_shrunken.exterior.xy
plt.plot(x,y)

# to net let the image be distorted along the axis
plt.axis('equal')

plt.show()


【讨论】:

    【解决方案4】:

    我已经在加利福尼亚州的 1200 多个现实生活中的建筑物的多边形上测试了这个解决方案,它的效果非常好。

    另一件事是,同样的方法也同样适用于放大多边形。 下面这个方法可以直接使用:

    def shrink_or_swell_shapely_polygon(my_polygon, factor=0.10, swell=False):
        ''' returns the shapely polygon which is smaller or bigger by passed factor.
            If swell = True , then it returns bigger polygon, else smaller '''
        from shapely import geometry
    
        #my_polygon = mask2poly['geometry'][120]
    
        shrink_factor = 0.10 #Shrink by 10%
        xs = list(my_polygon.exterior.coords.xy[0])
        ys = list(my_polygon.exterior.coords.xy[1])
        x_center = 0.5 * min(xs) + 0.5 * max(xs)
        y_center = 0.5 * min(ys) + 0.5 * max(ys)
        min_corner = geometry.Point(min(xs), min(ys))
        max_corner = geometry.Point(max(xs), max(ys))
        center = geometry.Point(x_center, y_center)
        shrink_distance = center.distance(min_corner)*0.10
    
        if swell:
            my_polygon_resized = my_polygon.buffer(shrink_distance) #expand
        else:
            my_polygon_resized = my_polygon.buffer(-shrink_distance) #shrink
    
        #visualize for debugging
        #x, y = my_polygon.exterior.xy
        #plt.plot(x,y)
        #x, y = my_polygon_shrunken.exterior.xy
        #plt.plot(x,y)
        ## to net let the image be distorted along the axis
        #plt.axis('equal')
        #plt.show()    
        
        return my_polygon_resized
    

    【讨论】:

      猜你喜欢
      • 2012-10-31
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      • 2013-02-07
      • 2017-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多