(Originally on camdp.com)

While designing my new apartment, I found a very cool use of machine learning. Yes, that's right, you can use machine learning in interior design. As crazy as it sounds, it is completely legitimate.

The Problem

I like to show off my collection of books. And with nice new bookshelves in my apartment, I wanted to display the book collection in an interesting way. I've seen organizing books by colour and gradient, a visual trick I find really appealing.

But creating a smooth gradient from colors is quite difficult, as colour changes strangely and the number of possible orderings rises quickly with increased number of books. One thing I can determine is the rgb colour scheme of each book. This gives me three dimensional data points associated with each book. What I would like to do is the following:

  1. Find a 1-dimensional representation of each book's color, so the collection can be sorted.
  2. Determine some ordering. Consider the following ordering. An ordering that sorts based on the amount of blue in a collection of books that are only green and red would be very poor (under an rgb scheme, remember, these books have little or no blue), as the human eye would have difficulty seeing the amount of blue vary over the ordering. Thus, I would like an ordering that maximizes the amount of variation in the collection.

This is otherwise known as PCA. So, simply performing PCA on my 3-d data points and projecting onto the principle component gives me an ordering that satisfies maximizing variation.

Results

This is an actual problem I had, and I used the PCA technique on a subset of my collection of books. I took a picture of 21 books and color-picked them to find the rgb values. I threw this all into Python to perform the PCA.

import numpy as np

colours = np.array( [
            [27,17,20],[121, 42, 65], [79, 15, 19], [79,23, 17],
            [70, 15,11], [84, 29, 27], [123, 44, 32], [138, 44, 34],
            [111, 28, 21], [86,21,16], [203, 65, 46], [193, 91, 63],
            [107, 24, 17], [109,44, 30],  [82, 24, 17], [127, 63, 33],
            [176, 97, 36], [155, 76, 32], [144, 91, 36], [185, 125, 50],
            [176, 140, 47], [174, 139, 87]
            ])
            
ncolors = (colours - colours.mean(axis=0 ) )/colours.std(axis=0) #always a smart idea to standardize pre-PCA
S = np.corrcoef( ncolours.T )
w,eignvectors = np.linalg.eig(S)

reduced_data = np.dot( ncolors, eignvectors[:,np.argmax(w)] )
collection = np.argsort( reduced_data )


In the figure below, the top row was my initial attempt to order the colors. As you can see, it is ok. The bottom row is the PCA's result. You can judge who did a better job

My ordering                                            
PCA's results                                            

The technique does seem to fail if there are errant colors in the collection. Above, probably the light-purple book belongs on a different bookshelf. However, the PCA ordering does do a good job of smoothing the gradient.

I projected the colours onto the first principle component, which is is $[0.57, 0.59, 0.56]$. Notice how close the values are. This means that the greatest variation in the colors is on the line that (close to) uniformly increases each rgb component. If you understand the rgb colour scale, this is equivalent to lightening vs darkening. So the above gradient is actually an ordering based on darkness level.

This technique works surprisingly well, even considering how strange colours can transition. For example, below I generated 21 different colours from the jet colourmap. Here is the sample:

                                         

And the PCA-ordered results are below:

                                         

Fuck yea. It seems to order the colours quite well.

Conclusion

This seems to be a pretty good way to order colors into visually appealing gradients. Thanks, math, for making my life just a little bit easier.