<p>从mgc展开上面的有用答案,然后再次使用<a href="https://stackoverflow.com/a/43023639/855617">https://stackoverflow.com/a/43023639/855617</a>中的<code>voronoi_finite_polygons_2d</code>,这里是一个将Voronoi镶嵌剪裁为任意形状(这里是从二进制掩码)的解决方案。这里唯一的额外工作是从你的面具制作一个多边形。我相信有其他(也许更好的)方法可以使面具像这样一夫多妻,但这对我的目的是有效的。</p>
<pre><code>import matplotlib.pyplot as plt
import numpy as np
from scipy.ndimage.morphology import binary_erosion
from scipy.spatial import Voronoi
from shapely.geometry import Point, Polygon
from skimage import draw
from sklearn.neighbors import KDTree
def get_circular_se(radius=2):
N = (radius * 2) + 1
se = np.zeros(shape=[N,N])
for i in range(N):
for j in range(N):
se[i,j] = (i - N / 2)**2 + (j - N / 2)**2 <= radius**2
se = np.array(se, dtype="uint8")
return se
def polygonize_by_nearest_neighbor(pp):
"""Takes a set of xy coordinates pp Numpy array(n,2) and reorders the array to make
a polygon using a nearest neighbor approach.
"""
# start with first index
pp_new = np.zeros_like(pp)
pp_new[0] = pp[0]
p_current_idx = 0
tree = KDTree(pp)
for i in range(len(pp) - 1):
nearest_dist, nearest_idx = tree.query([pp[p_current_idx]], k=4) # k1 = identity
nearest_idx = nearest_idx[0]
# finds next nearest point along the contour and adds it
for min_idx in nearest_idx[1:]: # skip the first point (will be zero for same pixel)
if not pp[min_idx].tolist() in pp_new.tolist(): # make sure it's not already in the list
pp_new[i + 1] = pp[min_idx]
p_current_idx = min_idx
break
pp_new[-1] = pp[0]
return pp_new
#generates a circular mask
side_len = 512
rad = 100
mask = np.zeros(shape=(side_len, side_len))
rr, cc = draw.circle(side_len/2, side_len/2, radius=rad, shape=mask.shape)
mask[rr, cc] = 1
#makes a polygon from the mask perimeter
se = get_circular_se(radius=1)
contour = mask - binary_erosion(mask, structure=se)
pixels_mask = np.array(np.where(contour==1)[::-1]).T
polygon = polygonize_by_nearest_neighbor(pixels_mask)
polygon = Polygon(polygon)
#generates random seeds
points_x = np.random.random_integers(0,side_len,250)
points_y = np.random.random_integers(0,side_len,250)
points = (np.vstack((points_x,points_y))).T
# returns a list of the centroids that are contained within the polygon
new_points = []
for point in points:
if polygon.contains(Point(point)):
new_points.append(point)
#performs voronoi tesselation
if len(points) > 3: #otherwise the tesselation won't work
vor = Voronoi(new_points)
regions, vertices = voronoi_finite_polygons_2d(vor)
#clips tesselation to the mask
new_vertices = []
for region in regions:
poly_reg = vertices[region]
shape = list(poly_reg.shape)
shape[0] += 1
p = Polygon(np.append(poly_reg, poly_reg[0]).reshape(*shape)).intersection(polygon)
poly = (np.array(p.exterior.coords)).tolist()
new_vertices.append(poly)
#plots the results
fig, ax = plt.subplots()
ax.imshow(mask,cmap='Greys_r')
for poly in new_vertices:
ax.fill(*zip(*poly), alpha=0.7)
ax.plot(points[:,0],points[:,1],'ro',ms=2)
plt.show()
</code></pre>
<p><a href="https://i.stack.imgur.com/qYpU3.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/qYpU3.png" alt="Voronoi tesselation clipped to a circular mask"/></a></p>