[英]render Voronoi diagram to numpy array
I'd like to generate Voronoi regions, based on a list of centers and an image size.我想根据中心列表和图像大小生成 Voronoi 区域。
I'm tryed the next code, based on https://rosettacode.org/wiki/Voronoi_diagram我尝试了下一个代码,基于https://rosettacode.org/wiki/Voronoi_diagram
def generate_voronoi_diagram(width, height, centers_x, centers_y):
image = Image.new("RGB", (width, height))
putpixel = image.putpixel
imgx, imgy = image.size
num_cells=len(centers_x)
nx = centers_x
ny = centers_y
nr,ng,nb=[],[],[]
for i in range (num_cells):
nr.append(randint(0, 255));ng.append(randint(0, 255));nb.append(randint(0, 255));
for y in range(imgy):
for x in range(imgx):
dmin = math.hypot(imgx-1, imgy-1)
j = -1
for i in range(num_cells):
d = math.hypot(nx[i]-x, ny[i]-y)
if d < dmin:
dmin = d
j = i
putpixel((x, y), (nr[j], ng[j], nb[j]))
image.save("VoronoiDiagram.png", "PNG")
image.show()
I have the desired output:我有所需的输出:
But it takes too much to generate the output.但是生成输出需要太多时间。
I also tried https://stackoverflow.com/a/20678647 It is fast, but I didn't find the way to translate it to numpy array of img_width X img_height.我也试过https://stackoverflow.com/a/20678647它很快,但我没有找到将其转换为 img_width X img_height 的 numpy 数组的方法。 Mostly, because I don't know how to give image size parameter to scipy Voronoi class .大多数情况下,因为我不知道如何为 scipy Voronoi class提供图像大小参数。
Is there any faster way to have this output?有没有更快的方法来获得这个输出? No centers or polygon edges are needed不需要中心或多边形边
Thanks in advance提前致谢
Edited 2018-12-11: Using @tel "Fast Solution" 2018-12-11 编辑:使用@tel “快速解决方案”
The code execution is faster, it seems that the centers have been transformed.代码执行速度更快,似乎中心已经转换。 Probably this method is adding a margin to the image可能这种方法是为图像添加边距
Here's how you can convert the output of the fast solution based on scipy.spatial.Voronoi
that you linked to into a Numpy array of arbitrary width and height.以下是如何将基于scipy.spatial.Voronoi
的快速解决方案的输出转换为任意宽度和高度的 Numpy 数组。 Given the set of regions, vertices
that you get as output from the voronoi_finite_polygons_2d
function in the linked code, here's a helper function that will convert that output to an array:给定一组regions, vertices
您从链接代码中的voronoi_finite_polygons_2d
函数获得的regions, vertices
,这里有一个辅助函数,可以将该输出转换为数组:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
def vorarr(regions, vertices, width, height, dpi=100):
fig = plt.Figure(figsize=(width/dpi, height/dpi), dpi=dpi)
canvas = FigureCanvas(fig)
ax = fig.add_axes([0,0,1,1])
# colorize
for region in regions:
polygon = vertices[region]
ax.fill(*zip(*polygon), alpha=0.4)
ax.plot(points[:,0], points[:,1], 'ko')
ax.set_xlim(vor.min_bound[0] - 0.1, vor.max_bound[0] + 0.1)
ax.set_ylim(vor.min_bound[1] - 0.1, vor.max_bound[1] + 0.1)
canvas.draw()
return np.frombuffer(canvas.tostring_rgb(), dtype='uint8').reshape(height, width, 3)
Here's a complete example of vorarr
in action:这是一个完整的vorarr
示例:
from scipy.spatial import Voronoi
# get random points
np.random.seed(1234)
points = np.random.rand(15, 2)
# compute Voronoi tesselation
vor = Voronoi(points)
# voronoi_finite_polygons_2d function from https://stackoverflow.com/a/20678647/425458
regions, vertices = voronoi_finite_polygons_2d(vor)
# convert plotting data to numpy array
arr = vorarr(regions, vertices, width=1000, height=1000)
# plot the numpy array
plt.imshow(arr)
Output:输出:
As you can see, the resulting Numpy array does indeed have a shape of (1000, 1000)
, as specified in the call to vorarr
.如您所见,生成的 Numpy 数组确实具有(1000, 1000)
的形状,如对vorarr
的调用中所指定。
Here's how you could alter your current code to work with/return a Numpy array:以下是如何更改当前代码以使用/返回 Numpy 数组:
import math
import matplotlib.pyplot as plt
import numpy as np
def generate_voronoi_diagram(width, height, centers_x, centers_y):
arr = np.zeros((width, height, 3), dtype=int)
imgx,imgy = width, height
num_cells=len(centers_x)
nx = centers_x
ny = centers_y
randcolors = np.random.randint(0, 255, size=(num_cells, 3))
for y in range(imgy):
for x in range(imgx):
dmin = math.hypot(imgx-1, imgy-1)
j = -1
for i in range(num_cells):
d = math.hypot(nx[i]-x, ny[i]-y)
if d < dmin:
dmin = d
j = i
arr[x, y, :] = randcolors[j]
plt.imshow(arr.transpose(1, 0, 2))
plt.scatter(cx, cy, c='w', edgecolors='k')
plt.show()
return arr
Example usage:用法示例:
np.random.seed(1234)
width = 500
cx = np.random.rand(15)*width
height = 300
cy = np.random.rand(15)*height
arr = generate_voronoi_diagram(width, height, cx, cy)
Example output:示例输出:
A fast solution without using matplotlib is also possible.不使用 matplotlib 的快速解决方案也是可能的。 Your solution is slow because you're iterating over all pixels, which incurs a lot of overhead in Python.您的解决方案很慢,因为您要遍历所有像素,这会在 Python 中产生大量开销。 A simple solution to this is to compute all distances in a single numpy operation and assigning all colors in another single operation.对此的一个简单解决方案是在单个 numpy 操作中计算所有距离,并在另一个单个操作中分配所有颜色。
def generate_voronoi_diagram_fast(width, height, centers_x, centers_y):
# Create grid containing all pixel locations in image
x, y = np.meshgrid(np.arange(width), np.arange(height))
# Find squared distance of each pixel location from each center: the (i, j, k)th
# entry in this array is the squared distance from pixel (i, j) to the kth center.
squared_dist = (x[:, :, np.newaxis] - centers_x[np.newaxis, np.newaxis, :]) ** 2 + \
(y[:, :, np.newaxis] - centers_y[np.newaxis, np.newaxis, :]) ** 2
# Find closest center to each pixel location
indices = np.argmin(squared_dist, axis=2) # Array containing index of closest center
# Convert the previous 2D array to a 3D array where the extra dimension is a one-hot
# encoding of the index
one_hot_indices = indices[:, :, np.newaxis, np.newaxis] == np.arange(centers_x.size)[np.newaxis, np.newaxis, :, np.newaxis]
# Create a random color for each center
colors = np.random.randint(0, 255, (centers_x.size, 3))
# Return an image where each pixel has a color chosen from `colors` by its
# closest center
return (one_hot_indices * colors[np.newaxis, np.newaxis, :, :]).sum(axis=2)
Running this function on my machine obtains a ~10x speedup relative to the original iterative solution (not taking plotting and saving the result to disk into account).在我的机器上运行此函数可以获得相对于原始迭代解决方案的约 10 倍加速(不考虑绘图并将结果保存到磁盘)。 I'm sure there are still a lot of other tweaks which could further accelerate my solution.我确信还有很多其他的调整可以进一步加速我的解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.