簡體   English   中英

將 Voronoi 圖渲染為 numpy 數組

[英]render Voronoi diagram to numpy array

我想根據中心列表和圖像大小生成 Voronoi 區域。

我嘗試了下一個代碼,基於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()

我有所需的輸出:

在此處輸入圖片說明

但是生成輸出需要太多時間。

我也試過https://stackoverflow.com/a/20678647它很快,但我沒有找到將其轉換為 img_width X img_height 的 numpy 數組的方法。 大多數情況下,因為我不知道如何為 scipy Voronoi class提供圖像大小參數。

有沒有更快的方法來獲得這個輸出? 不需要中心或多邊形邊

提前致謝

2018-12-11 編輯:使用@tel “快速解決方案”

在此處輸入圖片說明

代碼執行速度更快,似乎中心已經轉換。 可能這種方法是為圖像添加邊距

快速解決方案

以下是如何將基於scipy.spatial.Voronoi快速解決方案的輸出轉換為任意寬度和高度的 Numpy 數組。 給定一組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)

測試一下

這是一個完整的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 數組確實具有(1000, 1000)的形狀,如對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

用法示例:

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)

示例輸出:

在此處輸入圖片說明

不使用 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 倍加速(不考慮繪圖並將結果保存到磁盤)。 我確信還有很多其他的調整可以進一步加速我的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM