Color palette generation algorithm



Looking for a beautiful color palette for the site? Recently installed home RGB-lights, or want to paint the room in new colors? Or bought a keyboard with color backlight and want to use it to the fullest? Whatever the situation you are in, you are probably constantly setting up color schemes.

As a programmer, I quickly wrote a few lines of code to generate random color palettes. Immediately realizing that this approach may not give the best results, I realized the “reset” button of the palette in a couple of minutes. It seemed to me that to get a great scheme you just need a little luck and patience.

I was wrong. Generating palettes from random colors sucks. From time to time a beautiful color coexists with an ugly, dirty shade of brown or yellow. Selections of colors are always either too dark, or too light and low-contrast, or sets consist of very similar colors. It was necessary to come up with another solution.

Color spaces


Let's start with the theory. Today, the following color spaces are widely used to classify colors:

sRGB


RGB stands for Red Green Blue . This is how displays work: they emit light in three color channels, which are mixed in different proportions to produce all kinds of colors. The value in each channel ranges from 0 to 255. R:0, G:0, B:0 (or # 000000 in hexadecimal) is black, and R:255, G:255, B:255 (or #ffffff ) - white.

CIE Lab


The CIE Lab color space is wider than sRGB and includes all colors perceived by a person. It was created with the expectation of universality of perception. In other words, the distance between the colors corresponds to the subjective difference: if the values ​​of the two colors are close to each other, then they look similar. On the other hand, two colors far apart are also perceived as quite unlike. In CIE Lab for saturated colors allocated more space than for dark and light. By the way, for the human eye is very dark green almost indistinguishable from black. In addition, this three-dimensional color space: L means lightness (from 0.0 to 1.0), a and b (approximately -1.0 to 1.0) are color channels.

HCL


If RGB describes how the display displays colors, and CIE Lab describes how we perceive them, then HCL is the color space that most closely describes how we think about colors. It is also three-dimensional, H means color tone (hue) (from 0 to 360 degrees), - saturation (chroma) and L - brightness (luminance) (both parameters are measured from 0.0 to 1.0).

For calculations, I recommend using CIE Lab, and for presenting palettes to the user - HCL. If you wish, you can convert the values ​​from these spaces to RGB.

Color space decomposition


Since I needed to get a set of unique, individual colors, we first discard those that look very similar. The color space will be three-dimensional, and the clustering algorithm using the k-means method is perfect for separating such low-dimensional data sets. He tries to decompose the data (in our case, the color space) into k separate regions. And then the palette is collected from the central points of the clusters in these areas. The gif shows a two-dimensional mapping of the operation of the algorithm in the three-dimensional space of the CIE Lab.

Write the code


Using the k-means algorithm implemented on Go, the problem is solved with just a few lines of code. First, prepare the color values ​​in the CIE Lab space:

 var d clusters.Observations for l := 0.2; l <= 0.8; l += 0.05 { for a := -1.0; a < 1.0; a += 0.1 { for b := -1.0; b < 1.0; b += 0.1 { d = append(d, clusters.Coordinates{l, a, b}) } } } 

I already picked up a couple of parameters and imposed certain restrictions on the generated colors. In this example, we throw out colors that are too dark (brightness <0.2) and too bright (brightness> 0.8).

Expand the newly created color space:

 km := kmeans.New() clusters, _ := km.Partition(d, 8) 

The Partition function will return pieces of eight clusters. Each cluster has a Center point, which is a separate color in a given space. Its coordinates can easily be converted to a hexadecimal RGB value:

 col := colorful.Lab(c.Center[0], c.Center[1], c.Center[2]) col.Clamped().Hex() 

Remember that CIE Lab is wider than RGB, and therefore some Lab values ​​cannot be converted to RGB. Such values ​​can be transformed into the most similar colors of RGB-space with the help of Clamped .

Full code


 package main import ( "github.com/muesli/kmeans" "github.com/muesli/clusters" colorful "github.com/lucasb-eyer/go-colorful" ) func main() { // Create data points in the CIE L*a*b* color space // l for lightness, a & b for color channels var d clusters.Observations for l := 0.2; l <= 0.8; l += 0.05 { for a := -1.0; a <= 1.0; a += 0.1 { for b := -1.0; b <= 1.0; b += 0.1 { d = append(d, clusters.Coordinates{l, a, b}) } } } // Partition the color space into 8 clusters km := kmeans.New() clusters, _ := km.Partition(d, 8) for _, c := range clusters { col := colorful.Lab(c.Center[0], c.Center[1], c.Center[2]) fmt.Println("Color as Hex:", col.Clamped().Hex()) } } 

A set of eight (not so) random colors generated by this code:



Define your own color space


Add more control over the generation of colors. We can easily manage the data used for further calculations, thereby selecting the color space for your needs. Generate a pastel palette:

 func pastel(c colorful.Color) bool { _, s, v := col.Hsv() return 0.2 <= s && s <= 0.4 && 0.7 <= v && v <= 1.0 } for l := 0.0; l <= 1.0; l += 0.05 { for a := -1.0; a <= 1.0; a += 0.1 { for b := -1.0; b <= 1.0; b += 0.1 { col := colorful.Lab(l, a, b) if col.IsValid() && pastel(col) { d = append(d, clusters.Coordinates{l, a, b}) } } } } 

Another color space is HSV , the letters in the name indicate hue, saturation, and value. In this space, pastel colors usually have high brightness values ​​and low saturation values.

Here's what happened:



You can also filter colors by their saturation (chroma) and brightness to get a set of “warm” tones:

 func warm(col colorful.Color) bool { _, c, l := col.Hcl() return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5 } 

Result:



Package gamut


I am working on a library called gamut , in which I will put all the pieces described here in one convenient package on Go, which allows us to generate and manage color palettes and themes. You can already try it, but it is still in work.

Source: https://habr.com/ru/post/414455/


All Articles