Building a colour palette from an image.

In a fit of constructive procrastination I have programmed a function to extract colour information from a JPEG image and make a colour palette. This was inspired partly by Karthik Ram’s Wes Anderson R package.

This is how it works. First find yourself a nice image, then use the function, palettebuildr(), to extract the dominant colours. The function extracts the red/green/blue (rgb) colour values for each pixel in the image. It then calculates a distance matrix for the similarity of the colour values of each pixel. i.e. different shades of red will show up as being similar, likewise for shades of green etc. The function feeds this distance matrix into a clustering algorithm which hierarchically clusters the n pixels into n groups, based on similarity, to produce a tree object. It then cuts this tree into groups based on the desired number of groups. I used a similar algorithm to divide individual sheep into sub-groups based on their geographical location in one of the papers from my PhD (Jones et al. 2006 J. Anim. Ecol.). Once the pixels have been assigned to groups the function simply takes an arithmetic mean of the r/g/b colour values across the members of the group.

The arguments to the function are (1) the path to the JPEG file, (2) the number of colours desired, (3) the distance algorithm, (4) the clustering algorithm. The defaults for the latter two arguments are “euclidian” and “complete” – see the respective functions dist() and hclust() for details. It is worth exploring combinations of the available algorithms in some cases, depending on what you’re looking for, but the defaults seem to work well in most cases.

You run the function like this:

colorPalette <- palettebuildr("starrynight.jpg", ncols = 5)

Here’s a couple of examples.

Van Gogh’s “The Starry Night”


A still from Princess Mononoke


A bag of Skittles (hat tip Alyssa Frazee)


These palettes are naturally divergent, and might make a fun alternative to the excellent RColorBrewer colour schemes.

Here is the function, saved as a gist on github

palettebuildr <- function(pathToJPEG = "logo.jpg", ncols = 3, dist.method = "euclidian", clust.method = "complete"){
#Read in the jpeg file
img <- readJPEG(pathToJPEG)
#Using the whole image is overkill, especially for large files.
#Therefore, create a grid from which extract the colors
xgrid <- ceiling(seq(1, dim(img)[1], length.out = 50))
ygrid <- ceiling(seq(1, dim(img)[2], length.out = 50))
pixels <- expand.grid(xgrid, ygrid)
#Get the red/green/blue values for each pixel
pixels$redVals <- apply(pixels, 1, function(x){return(img[x[1], x[2], ][1])})
pixels$greenVals <- apply(pixels, 1, function(x){return(img[x[1], x[2], ][2])})
pixels$blueVals <- apply(pixels, 1, function(x){return(img[x[1] ,x[2], ][3])})
#Get the euclidian distances of the colour values.
#This creates a distance matrix showing how similar the pixels
#are to eachother.
distMat <- dist(pixels[, 3:5], method = dist.method)
#Use hierarchical clustering to group the colours into n groups
x <- hclust(distMat,method = clust.method)
#Assign each pixel to one of k-groups based on clustering
pixels$groups <- cutree(x, k = ncols)
#Take average red/green/blue values for the whole group
redGroup <- tapply(pixels$redVals, pixels$groups, mean)
greenGroup <- tapply(pixels$greenVals, pixels$groups, mean)
blueGroup <- tapply(pixels$blueVals, pixels$groups, mean)
#Create a dataframe of colours to return
groupCols <- data.frame(gp = 1:ncols, red = redGroup, green = greenGroup, blue=blueGroup)
groupCols$hexCode <- rgb(red = groupCols$red, green = groupCols$green, blue = groupCols$blue, alpha = 1, maxColorValue = 1)

view raw


hosted with ❤ by GitHub

Edit 25/2/2015 – Here’s another great example by Russell Dinnage (@ecologician) of colour palette extraction from images. This time using phylogenetic methods to generate a palette which takes into account human perception/colour luminosity.

1 thought on “Building a colour palette from an image.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s