import numpy as np
from PIL import Image
from scipy.spatial import cKDTree, Delaunay
from skimage.draw import line
def generate_structured_voronoi_delaunay(
width=4096, height=4096, num_sites=2000, seed=42
):
np.random.seed(seed)
# 1) Generate random sites
sites = np.column_stack([
np.random.randint(0, width, size=num_sites),
np.random.randint(0, height, size=num_sites)
])
# 2) Voronoi labeling
tree = cKDTree(sites)
xv, yv = np.meshgrid(np.arange(width), np.arange(height))
pts = np.column_stack([xv.ravel(), yv.ravel()])
labels = tree.query(pts, k=1)[1].reshape((height, width))
# 3) Delaunay edges → pixel list
tri = Delaunay(sites)
edge_set = set()
for simplex in tri.simplices:
for (i, j) in [(0,1), (1,2), (2,0)]:
x0, y0 = sites[simplex[i]]
x1, y1 = sites[simplex[j]]
rr, cc = line(int(y0), int(x0), int(y1), int(x1))
valid = (rr>=0)&(rr=0)&(cc>16)&0xFF
G = (all_colors>>8)&0xFF
B = all_colors&0xFF
palette = np.column_stack([R,G,B]).astype(np.uint8)
# 5) Prepare the output image
img = np.zeros((height, width, 3), dtype=np.uint8)
# 6) Paint edges as a luminance‐sorted gradient
edge_colors = palette[:num_edge_pixels]
# compute luminance
lum_e = 0.299*edge_colors[:,0] + 0.587*edge_colors[:,1] + 0.114*edge_colors[:,2]
sorted_colors_e = edge_colors[np.argsort(lum_e)]
# sort edge coords in raster order
flat_idx = edges[:,0]*width + edges[:,1]
scan_order = np.argsort(flat_idx)
for idx, e_i in enumerate(scan_order):
r, c = edges[e_i]
img[r, c] = sorted_colors_e[idx]
# mark those pixels off‐limits for region filling
is_edge = np.zeros(total_pixels, dtype=bool)
is_edge[edges[:,0]*width + edges[:,1]] = True
# 7) Fill each Voronoi region with its own radial luminance gradient
ptr = num_edge_pixels
flat_labels = labels.ravel()
for site_idx in range(num_sites):
# all pixels in this region (flat indexes)
region_mask = flat_labels == site_idx
region_mask[is_edge] = False
pix_idxs = np.nonzero(region_mask)[0]
n = len(pix_idxs)
if n == 0:
continue
# slice off this region's colors and sort by luminance
block = palette[ptr:ptr+n]
ptr += n
lum_b = 0.299*block[:,0] + 0.587*block[:,1] + 0.114*block[:,2]
sorted_block = block[np.argsort(lum_b)]
# get (r,c) coords and sort them by distance to the site
rs = pix_idxs // width
cs = pix_idxs % width
dy = rs - sites[site_idx,1]
dx = cs - sites[site_idx,0]
dists = np.hypot(dx, dy)
order = np.argsort(dists)
# paint
for color, idx in zip(sorted_block, order):
r = rs[idx]
c = cs[idx]
img[r, c] = color
# 8) Save result
Image.fromarray(img).save("structured_voronoi_delaunay_4096.png")
print("Saved structured_voronoi_delaunay_4096.png")
if __name__ == "__main__":
generate_structured_voronoi_delaunay()