Voronoi cells are basically the shape you see soap suds make. They have a lot of cool properties that I wanted to use for image generation, but I didn’t want to have to figure out the math myself. I found this really excellent tutorial on generating Voronoi cells, which goes into some interesting history about them, too!
However, the Python code was a little out-of-date (and I think the author’s primary language was C++), so I wanted to clean up the example a bit.
It’s always a little tricky combining numpy and cv2, since numpy is column-major and cv2 is row-major (or maybe visa versa?) so I’m doing a rectangle instead of a square to make sure the coordinates are all ordered correctly. I started with some initialization:
import cv2 from matplotlib import pyplot as plt import numpy as np random.seed(42) width = 256 height = 128 num_points = 25
Then we can use the Subdiv2D class and add a point for each cell:
subdiv = cv2.Subdiv2D((0, 0, width, height)) def RandomPoint(): return (int(random.random() * width), int(random.random() * height)) for i in range(num_points): subdiv.insert(RandomPoint())
Then it just spits out the cells!
# Note that this is height x width! img = np.zeros((height, width, 3), dtype=np.uint8) def RandomColor(): """Generates a random RGB color.""" return ( random.randint(0, 256), random.randint(0, 256), random.randint(0, 256)) # idx is the list of indexes you want to get,  means all. facets, centers = subdiv.getVoronoiFacetList(idx=) for facet, center in zip(facets, centers): # Convert shape coordinates (floats) to int. ifacet = np.array(facet, int) # Draw the polygon. cv2.fillConvexPoly(img, ifacet, RandomColor(), cv2.LINE_AA, 0) # Draw a black edge around the polygon. cv2.polylines(img, np.array([ifacet]), True, (0, 0, 0), 1, cv2.LINE_AA, 0) # Draw the center point of each cell. cv2.circle( img, (int(center), int(center)), 3, (0, 0, 0), cv2.FILLED, cv2.LINE_AA, 0)
Finally, write img to a file or just it display with:
If you use 42 as the seed, you should see exactly: