python 33 lines · 7 steps

Extracting a color palette with K-means

Cluster an image's pixels into a handful of dominant colors and rank them by how much of the image they cover.

Explained by highlit
1from collections import Counter
2 
3import numpy as np
4from PIL import Image
5from sklearn.cluster import KMeans
6 
7 
8def extract_palette(image_path, num_colors=6, resize_to=200, sample_frac=1.0):
9 image = Image.open(image_path).convert("RGB")
10 image.thumbnail((resize_to, resize_to))
11 
12 pixels = np.asarray(image, dtype=np.float64).reshape(-1, 3)
13 if sample_frac < 1.0:
14 rng = np.random.default_rng(seed=42)
15 idx = rng.choice(len(pixels), size=int(len(pixels) * sample_frac), replace=False)
16 pixels = pixels[idx]
17 
18 kmeans = KMeans(n_clusters=num_colors, n_init="auto", random_state=42)
19 labels = kmeans.fit_predict(pixels)
20 
21 counts = Counter(labels)
22 total = sum(counts.values())
23 centers = kmeans.cluster_centers_.round().astype(int)
24 
25 palette = []
26 for label, count in counts.most_common():
27 r, g, b = centers[label]
28 palette.append({
29 "hex": f"#{r:02x}{g:02x}{b:02x}",
30 "rgb": (int(r), int(g), int(b)),
31 "weight": round(count / total, 4),
32 })
33 return palette
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Clustering pixel colors turns thousands of shades into a few representative swatches.
  2. 2Downsizing and optionally subsampling keeps the clustering fast without changing the palette much.
  3. 3Cluster sizes double as weights, letting you rank colors by their visual prominence.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Extracting a color palette with K-means — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code