使用角坐标收缩多边形

时间:2018-03-29 14:17:43

标签: python opencv

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

enter image description here

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

enter image description here

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

4 个答案:

答案 0 :(得分:5)

我已经在加利福尼亚州的 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

enter image description here

答案 1 :(得分:2)

我误解了这个问题,我放弃了答案,因为它可能对某人有帮助,但我发现最终的输出不是理想的输出

要在缩小后获取多边形的新坐标,可以将所有坐标(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)

输出以下内容(蓝色为原始,绿色为收缩多边形)

enter image description here

答案 2 :(得分:2)

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

但是,如果已经用相同的x和y因子(或者作为更复杂的算法的开始)来完成此操作,则可以:

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

(如果您需要更多的地理数据特定功率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()


enter image description here

答案 3 :(得分:0)

我认为在数学上不可能在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获得了线路交叉码。

此输出 Figure of the output