SciPy创建2D多边形蒙版

时间:2010-09-06 21:05:17

标签: python scipy polygon sage point-in-polygon

我需要使用标准的Python包创建一个numpy 2D数组,它表示多边形的二进制掩码。

  • 输入:多边形顶点,图像尺寸
  • 输出:多边形的二进制掩码(numpy 2D数组)

(更大的上下文:我想使用scipy.ndimage.morphology.distance_transform_edt获取此多边形的距离变换。)

有人能告诉我怎么做吗?

6 个答案:

答案 0 :(得分:56)

答案很简单:

import numpy
from PIL import Image, ImageDraw

# polygon = [(x1,y1),(x2,y2),...] or [x1,y1,x2,y2,...]
# width = ?
# height = ?

img = Image.new('L', (width, height), 0)
ImageDraw.Draw(img).polygon(polygon, outline=1, fill=1)
mask = numpy.array(img)

答案 1 :(得分:24)

作为@ Anil答案的一个更直接的替代方法,matplotlib有matplotlib.nxutils.points_inside_poly可用于快速栅格化任意多边形。 E.g。

import numpy as np
from matplotlib.nxutils import points_inside_poly

nx, ny = 10, 10
poly_verts = [(1,1), (5,1), (5,9),(3,2),(1,1)]

# Create vertex coordinates for each grid cell...
# (<0,0> is at the top left of the grid in this system)
x, y = np.meshgrid(np.arange(nx), np.arange(ny))
x, y = x.flatten(), y.flatten()

points = np.vstack((x,y)).T

grid = points_inside_poly(points, poly_verts)
grid = grid.reshape((ny,nx))

print grid

产生(布尔numpy数组):

[[False False False False False False False False False False]
 [False  True  True  True  True False False False False False]
 [False False False  True  True False False False False False]
 [False False False False  True False False False False False]
 [False False False False  True False False False False False]
 [False False False False  True False False False False False]
 [False False False False False False False False False False]
 [False False False False False False False False False False]
 [False False False False False False False False False False]
 [False False False False False False False False False False]]

你应该能够很好地将grid传递给任何scipy.ndimage.morphology函数。

答案 2 :(得分:12)

Joe的评论更新。 自评论发布以来,Matplotlib API已发生变化,现在您需要使用子模块matplotlib.path提供的方法。

工作代码如下。

import numpy as np
from matplotlib.path import Path

nx, ny = 10, 10
poly_verts = [(1,1), (5,1), (5,9),(3,2),(1,1)]

# Create vertex coordinates for each grid cell...
# (<0,0> is at the top left of the grid in this system)
x, y = np.meshgrid(np.arange(nx), np.arange(ny))
x, y = x.flatten(), y.flatten()

points = np.vstack((x,y)).T

path = Path(poly_verts)
grid = path.contains_points(points)
grid = grid.reshape((ny,nx))

print grid

答案 3 :(得分:4)

您可以尝试使用python的图像库PIL。首先,初始化画布。然后创建一个绘图对象,然后开始创建线条。这假设多边形位于R ^ 2中,并且输入的顶点列表的顺序正确。

输入= [(x1,y1),(x2,y2),...,(xn,yn)],(宽度,高度)

from PIL import Image, ImageDraw

img = Image.new('L', (width, height), 0)   # The Zero is to Specify Background Color
draw = ImageDraw.Draw(img)

for vertex in range(len(vertexlist)):
    startpoint = vertexlist[vertex]
    try: endpoint = vertexlist[vertex+1]
    except IndexError: endpoint = vertexlist[0] 
    # The exception means We have reached the end and need to complete the polygon
    draw.line((startpoint[0], startpoint[1], endpoint[0], endpoint[1]), fill=1)

# If you want the result as a single list
# You can make a two dimensional list or dictionary by iterating over the height and width variable
list(img.getdata())

# If you want the result as an actual Image
img.save('polgon.jpg', 'JPEG')

这是你在寻找什么,或者你在问一些不同的东西?

答案 4 :(得分:3)

使用matplotlib.path作为@Yusuke N.答案的一种替代方法,与from PIL import Image, ImageDraw的效率一样(无需安装Pillow,无需考虑{ {1}}或integer。对我有用,哈?)

工作代码如下:

float

结果图像在下面,其中暗区import pylab as plt import numpy as np from matplotlib.path import Path width, height=2000, 2000 polygon=[(0.1*width, 0.1*height), (0.15*width, 0.7*height), (0.8*width, 0.75*height), (0.72*width, 0.15*height)] poly_path=Path(polygon) x, y = np.mgrid[:height, :width] coors=np.hstack((x.reshape(-1, 1), y.reshape(-1,1))) # coors.shape is (4000000,2) mask = poly_path.contains_points(coors) plt.imshow(mask.reshape(height, width)) plt.show() 亮区Falseenter image description here

答案 5 :(得分:0)

这是一个实现@IsaacSutherland方法(已接受的答案)的函数,并进行了一些我认为有用的修改。欢迎发表评论!

poly_mask()接受多个多边形作为输入,以便输出蒙版可以由多个最终不连接的多边形区域组成。 此外,由于在某些情况下0并不是用于屏蔽的好值(例如,如果0是要对其应用掩码的数组的有效值),我添加了一个value关键字来设置实际的屏蔽值(例如一个非常小的/很大的数字或NAN):要实现此目的,掩码将转换为float数组。

def poly_mask(shape, *vertices, value=np.nan):
"""
Create a mask array filled with 1s inside the polygon and 0s outside.
The polygon is a list of vertices defined as a sequence of (column, line) number, where the start values (0, 0) are in the
upper left corner. Multiple polygon lists can be passed in input to have multiple,eventually not connected, ROIs.
    column, line   # x, y
    vertices = [(x0, y0), (x1, y1), ..., (xn, yn), (x0, y0)] or [x0, y0, x1, y1, ..., xn, yn, x0, y0]
Note: the polygon can be open, that is it doesn't have to have x0,y0 as last element.

adapted from: https://stackoverflow.com/questions/3654289/scipy-create-2d-polygon-mask/64876117#64876117
:param shape:    (tuple) shape of the output array (height, width)
:param vertices: (list of tuples of int): sequence of vertices defined as
                                           [(x0, y0), (x1, y1), ..., (xn, yn), (x0, y0)] or
                                           [x0, y0, x1, y1, ..., xn, yn, x0, y0]
                                           Multiple lists (for multiple polygons) can be passed in input
:param value:    (float or NAN)      The masking value to use (e.g. a very small number). Default: np.nan
:return:         (ndarray) the mask array
"""
width, height = shape[::-1]
# create a binary image
img = Image.new(mode='L', size=(width, height), color=0)  # mode L = 8-bit pixels, black and white
draw = ImageDraw.Draw(img)
# draw polygons
for polygon in vertices:
    draw.polygon(polygon, outline=1, fill=1)
# replace 0 with 'value'
mask = np.array(img).astype('float32')
mask[np.where(mask == 0)] = value
return mask

我更喜欢直接使用shape作为输入(而不是(宽度,高度)),这样我可以像这样使用它:

polygon_lists = [
    [(x0, y0), (x1, y1), ..., (xn, yn), (x0, y0)],
    [# ... another sequence of coordinates...],
    [# ...yet another sequence of coordinates...],
                ]
my_mask = poly_mask(my_array.shape, *polygon_lists)

其中my_array是必须将蒙版应用到的数组(当然是具有相同形状的另一个数组)。

my_array_masked = my_array * my_mask