Thumbnail.

Structured Voronoi Delaunay

Description

Another piece of “wiskunst,“ which is a portmanteau of the Dutch words wiskunde (meaning mathematics) and kunst (meaning art) — sadly, I can’t find a nice equivalent term in English. Made with Chatgpt o4-mini-high and Google Colab.

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()

Author

ACJ
43 entries

Stats

Date
Colors16,777,216
Pixels16,777,216
Dimensions4,096 × 4,096
Bytes50,269,888