Python 将Voronoi图渲染到numpy数组

Python 将Voronoi图渲染到numpy数组,python,numpy,scipy,voronoi,Python,Numpy,Scipy,Voronoi,我想根据中心列表和图像大小生成Voronoi区域 我正在尝试下一个代码,基于 def生成voronoi图(宽度、高度、中心x、中心y): image=image.new(“RGB”(宽度、高度)) putpixel=image.putpixel imgx,imgy=image.size num_cells=len(中心x) nx=中心x ny=中心y nr、ng、nb=[]、[]、[] 对于范围内的i(num_单元格): nr.append(randint(0255));ng.append(ra

我想根据中心列表和图像大小生成Voronoi区域

我正在尝试下一个代码,基于

def生成voronoi图(宽度、高度、中心x、中心y):
image=image.new(“RGB”(宽度、高度))
putpixel=image.putpixel
imgx,imgy=image.size
num_cells=len(中心x)
nx=中心x
ny=中心y
nr、ng、nb=[]、[]、[]
对于范围内的i(num_单元格):
nr.append(randint(0255));ng.append(randint(0255));nb.append(randint(0255));
对于范围内的y(imgy):
对于范围内的x(imgx):
dmin=数学形波(imgx-1,imgy-1)
j=-1
对于范围内的i(num_单元格):
d=数学形下压(nx[i]-x,ny[i]-y)
如果d
我有理想的输出:

但是产生输出需要太多的时间

我也试过了 它很快,但我没有找到方法将其转换为img_宽度X img_高度的numpy数组。主要是因为我不知道如何将图像大小参数赋予scipy

有没有更快的方法获得这个输出?不需要中心或多边形边

提前谢谢

2018年12月11日编辑: 使用“快速解决方案”

代码执行速度更快,似乎中心已被转换。可能这种方法是在图像上添加一个边距

快速解决方案 以下是如何将链接到的的输出转换为任意宽度和高度的Numpy数组。给定从链接代码中的
voronoi_finite_polygons_2d
函数输出的一组
区域、顶点
,下面是一个将该输出转换为数组的辅助函数:

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)
测试它 下面是
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)
输出:

如您所见,生成的Numpy数组的形状确实是
(10001000)
,如调用
vorarr
中指定的

如果要修复现有代码 以下是如何更改当前代码以使用/返回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
示例输出:


不使用matplotlib的快速解决方案也是可能的。您的解决方案很慢,因为您要迭代所有像素,这会在Python中产生大量开销。一个简单的解决方案是在一个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)

在我的机器上运行此功能可获得约10倍于原始迭代解的加速(不考虑打印和将结果保存到磁盘)。我确信还有很多其他的调整可以进一步加快我的解决方案。

也许可以编译它?这是一个非常慢的算法,语言也非常慢。查看以计算Voronoi并制作图像。非常感谢@tel!我喜欢你的解决方案,但中心的位置似乎缩小了。我附加了您的解决方案输出及其中心,以及原始中心。也许matplotlib增加了利润?
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)
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)