# Generating wallpaper-consistent terminal colors with K-means++

## K-means algorithm

Assume that we have to following image:

We can represent the image in 3D-space as follows:

Suppose that we want to find eight dominant colors in the image. We could create a histogram and take the eight most common values. This would be fine, but often, very similar colors would repeat. Instead, we can try to find eight centroids in the image and find clusters of points beloning to each centroid (say, a point $p$ belongs to a centroid $p_{\text{centroid}}$ if $\|p - p_{\text{centroid}}\|_2 < R$ for some radius $R$). All points not belonging to a centroid will be penalized using some heuristic. This is basically the K-means clustering algorithm.

## Usage

The algorithm can be used to generate set of dominant colors to be used for instance in the terminal. Running the above algorithm on the image, we get

or as list

['#321331', '#e1a070', '#621d39', '#e05a40', '#9ed8aa', '#8b3860', '#f7d188', '#ab3031']

{
"color": [
"#ab3031","#621d39","#e05a40","#e1a070","#9ed8aa","#521f50","#8b3860","#f7d188","#ab5a5b","#623b4b","#e08c7b","#e1b99c","#c4d8c8","#715171","#8b5770","#f7e4c0"
],
"foreground": "#c5c8c6",
"background": "#282a2e"
}


In iTerm, it might look like this

This can be achieved using the following code:

#!/usr/bin/env python
import sys, os
from sklearn.cluster import KMeans
from PIL import Image

nbrcentroids = 10
# Constant increase in colors
beta = 10
# Multplicative factor in background
gamma = 0.4
rgb2hex = lambda rgb: '#%s' % ''.join(('%02x' % min(p + beta, 255) for p in rgb))
darken = lambda rgb : (p * gamma for p in rgb)

def getcentroids(filename, n=8):
img = Image.open(filename)
img.thumbnail((100, 100))
# Run K-means algorithm on image
kmeans = KMeans(init='k-means++', n_clusters=n)
kmeans.fit(list(img.getdata()))
# Get centroids from solution
rgbs = [map(int, c) for c in kmeans.cluster_centers_]
return rgbs

def set_colors_gnome(centroids):
centroids = sorted(centroids, key=lambda rgb: sum(c**2 for c in rgb))
prefix = 'gsettings set org.pantheon.terminal.settings '
# Set background and foreground
os.system(prefix + 'background \"%s\"' % rgb2hex(darken(centroids[0])))
os.system(prefix + 'foreground \"%s\"' % rgb2hex(centroids[-1]))
# Set ANSI colors
colors = ':'.join(rgb2hex(centroid) for centroid in centroids[1:-1])
os.system(prefix + 'palette \"' + colors + ':' + colors + '\"')

def bar(mode):
write = sys.stdout.write
for i in range(0, nbrcentroids):
write('\033[0;3%dmBAR ' % i)
write('\n')

centroids = getcentroids(sys.argv[1], n=nbrcentroids)
set_colors_gnome(centroids)
bar(0);


It is on Github too!