SciPy创建2D多边形遮罩

j2qf4p5b  于 2023-10-20  发布在  其他
关注(0)|答案(7)|浏览(135)

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

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

(大背景:我想使用scipy.ndimage.morphology.distance_transform_edt得到这个多边形的距离变换。)
有人能教我怎么做吗?

ivqmmu1c

ivqmmu1c1#

答案很简单:

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)
ycggw6v2

ycggw6v22#

作为@Anil的答案的一个稍微更直接的替代方案,matplotlib有matplotlib.nxutils.points_inside_poly,可以用来快速绘制任意多边形。例如

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函数。

hrysbysz

hrysbysz3#

关于乔的评论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
gjmwrych

gjmwrych4#

作为 *@Yusuke N. * 答案的一个轻微替代,考虑使用matplotlib.path,它与from PIL import Image, ImageDraw的效率一样高(不需要安装Pillow,不需要考虑integerfloat。有用的我?)
工作代码如下:

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()

结果图像如下,其中暗区False亮区True

pieyvz9o

pieyvz9o5#

你可以尝试使用python的图像库PIL。首先初始化画布。然后创建一个绘图对象,并开始绘制线条。这是假设多边形位于R^2中,并且输入的顶点列表顺序正确。
输入= [(x1,y1),(x2,y2),.,(xn,yn)],(width,height)

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')

这是你要找的东西吗,还是别的问题?

8i9zcol2

8i9zcol26#

下面是一个实现@IsaacSutherland方法(公认的答案)的函数,我发现一些有用的修改。欢迎评论!
poly_mask()接受多个多边形作为输入,以便输出掩码可以由多个最终不连接的多边形区域组成。此外,因为在某些情况下0不是用于掩蔽的好值(例如,如果0是要应用掩码的数组的有效值),我添加了一个value关键字,用于设置实际的掩码值(例如,一个非常小/大的数字或NAN):为了实现这一点,掩码被转换为浮点数组。

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

而不是(width,height),我更喜欢直接使用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
0h4hbjxa

0h4hbjxa7#

下面是一个cv2版本:

import cv2
import numpy as np

# Create mask
image_width = 800
image_height = 600
mask = np.zeros((image_height, image_width), dtype=np.uint8)

# Define the vertices of a polygon
polygon_vertices = np.array([
    [(100, 100), (300, 100), (200, 300)],
    [(400, 200), (600, 200), (500, 400)]
], dtype=np.int32)

# Draw filled polygons
cv2.fillPoly(mask, polygon_vertices, color=(255)) 

# Display the image with the filled polygons
cv2.imshow('Filled Polygons', mask)
cv2.waitKey(0)
cv2.destroyAllWindows()

# Save the image with filled polygons to a file
cv2.imwrite('filled_polygons.png', mask)

相关问题