从多边形点 Python 创建一个填充的 numpy 数组

Creating a filled numpy array of ones from a polygon points Python

如果有人能帮助我找到解决问题的更快方法,我将不胜感激。

场景如下:- 我有一个浮点多边形,我想将其映射到网格。网格单元可以是不同的宽度和高度,不像我的图像显示的那样统一。即矩形。

我试过使用 Image draw 但它只使用整数。将浮点数转换为整数意味着我必须按比例放大浮点数并删除小数点以保持一定的精度,但图像绘制不适用于较大的多边形点。

是否有更多 eloquent 和快速的方法来为多边形的填充区域实现 1(蓝色)的 numpy 数组,其余部分为零(红色)。我已经阅读了一些关于网格的内容,但看不到它如何用于这种情况。

非常感谢

代码的结果是

cols = 4
rows = 4
points = [[1535116L, 1725047L], [1535116L, 2125046L], [-464884L, 2125046L], [-464884L, 125046L]]    

bbCut = getPythonBoundBox(points)
cutWidth =  bbCut[1][0]-bbCut[0][0]
scale = float(cutWidth) / float(rows)
###Center data to origin
for p in range(len(points)):
    points[p][0] -= (bbCut[1][0] - bbCut[0][0])/2
    points[p][1] -= (bbCut[1][1] - bbCut[0][1])/2
    points[p][0] /= scale
    points[p][1] /= scale


##move points to Zero
bbCut = getPythonBoundBox(points)

for p in range(len(points)):
    points[p][0] -=bbCut[0][0]
    points[p][1] -=bbCut[0][1]

pointToTuple= []
for p in range(len(points)):
  pointToTuple.append((points[p][0], points[p][1]))

imgWidth = float(rows)
imgHeight = float(cols)

img = Image.new('L', (int(imgWidth), int(imgHeight)), 0) 
draw = ImageDraw.Draw(img)

draw.polygon(pointToTuple, fill=1)

array =  np.reshape(list(img.getdata()), (cols, rows))

############This is the result from the array############
##If you compare this array to the coloured scaled image ive have drawn
##its missing a 1 on the second value in the first row
##and another 1 on the second row 3rd value
##I'm assuming there is some parsing happening here with float to int?                       
array([1, 0, 0, 0])
array([1, 1, 0, 0])
array([1, 1, 1, 1])
array([1, 1, 1, 1])
#########################################################

def getPythonBoundBox(points):
    bigNumber = 10e10
    xmin = bigNumber
    xmax = -bigNumber
    ymin = bigNumber
    ymax = -bigNumber
    g = []
    a = len(points)
    for i in xrange(a):
        if points[i][0] < xmin: xmin = points[i][0]
        if points[i][0] > xmax: xmax = points[i][0]
        if points[i][1] < ymin: ymin = points[i][1]
        if points[i][1] > ymax: ymax = points[i][1]
    p1 = [xmin,ymin]
    g.append(p1)
    p2 = [xmax,ymax]
    g.append(p2)
    return (g)

matplotlib.path.Path 有一个方法 contains_points。因此,只需用您的多边形点实例化一条路径,然后检查您的网格坐标是否落在该路径内。您的网格可以具有您想要的任何分辨率。这由下面代码中的 nxny(或者 dxdy)控制。

代码:

import numpy as np
import matplotlib.pyplot as plt

from matplotlib.patches import PathPatch
from matplotlib.path import Path

# create a matplotlib path
points = [[1535116L, 1725047L],
          [1535116L, 2125046L],
          [-464884L, 2125046L],
          [-464884L, 125046L],
          [1535116L, 1725047L]]
codes = [Path.MOVETO,
         Path.LINETO,
         Path.LINETO,
         Path.LINETO,
         Path.CLOSEPOLY,
         ]
path = Path(points, codes)

# check the path
fig, (ax1, ax2, ax3) = plt.subplots(1,3)
patch = PathPatch(path, facecolor='k')
ax1.add_patch(patch)
xmin, ymin = np.min(points, axis=0)
xmax, ymax = np.max(points, axis=0)
ax1.set_ylim(ymin,ymax)
ax1.set_xlim(xmin,xmax)
ax1.set_aspect('equal')

# create a grid
nx, ny = 1000, 1000
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
xgrid, ygrid = np.meshgrid(x, y)
pixel_coordinates = np.c_[xgrid.ravel(), ygrid.ravel()]

# find points within path
img = path.contains_points(pixel_coordinates).reshape(nx,ny)

# plot
ax2.imshow(img, cmap='gray_r', interpolation='none', origin='lower')

# repeat, but this time specify pixel widths explicitly
dx, dy = 2000, 2000
x = np.arange(xmin, xmax, dx)
y = np.arange(ymin, ymax, dy)
xgrid, ygrid = np.meshgrid(x, y)
pixel_coordinates = np.c_[xgrid.ravel(), ygrid.ravel()]
img = path.contains_points(pixel_coordinates).reshape(len(x), len(y))
ax3.imshow(img, cmap='gray_r', interpolation='none', origin='lower')

更新:

好的,现在测试每个图块的角是否在路径内。出于某种原因,我仍然得到与图片所示不同的答案。您有多确定您提供的分数是准确的?

代码+图片:

import numpy as np
import matplotlib.pyplot as plt

from matplotlib.patches import PathPatch
from matplotlib.path import Path

# create a matplotlib path
points = [[1535116L, 1725047L],
          [1535116L, 2125046L],
          [-464884L, 2125046L],
          [-464884L, 125046L],
          [1535116L, 1725047L]]

codes = [Path.MOVETO,
         Path.LINETO,
         Path.LINETO,
         Path.LINETO,
         Path.CLOSEPOLY,
         ]
path = Path(points, codes)

fig, (ax1, ax2) = plt.subplots(1,2)
patch = PathPatch(path, facecolor='k')
ax1.add_patch(patch)
xmin, ymin = np.min(points, axis=0)
xmax, ymax = np.max(points, axis=0)
ax1.set_ylim(ymin,ymax)
ax1.set_xlim(xmin,xmax)
ax1.set_aspect('equal')

nx, ny = 4, 4
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
xgrid, ygrid = np.meshgrid(x, y)
pixel_centers = np.c_[xgrid.ravel(), ygrid.ravel()]

def pixel_center_to_corners(x, y, dx, dy, precision=0.):
    """
    Returns array indexed by (pixel, corner, (x,y))
    """

    # make dx and dy ever so slightly smaller,
    # such that the points fall **inside** the path (not **on** the path)
    dx -= precision
    dy -= precision

    return np.array([(x - dx/2., y - dy/2.), # lower left
                     (x + dx/2., y - dy/2.), # lower right
                     (x + dx/2., y + dy/2.), # upper right
                     (x - dx/2., y + dy/2.), # upper left
    ]).transpose([2,0,1])

# get pixel corners
dx = (xmax - xmin) / float(nx)
dy = (ymax - ymin) / float(ny)
pixel_corners = pixel_center_to_corners(pixel_centers[:,0], pixel_centers[:,1], dx, dy)

# test corners of each pixel;
# set img to True, iff any corners within path;
img = np.zeros((len(pixel_corners)))
for ii, pixel in enumerate(pixel_corners):
    is_inside_path = path.contains_points(pixel)
    img[ii] = np.any(is_inside_path)

img = img.reshape(len(x), len(y))
ax2.imshow(img, cmap='gray_r', interpolation='none', origin='lower')