So, I\'m working on a fresh iteration of a web app. And, we\'ve found that our users are obsessed with being lazy. Really lazy. In fact, the more wor
Average the hue, saturation and brightness separately while keeping the min/max values.
Lock the target hue of all the colours to the average and interpolate the saturation and brightness for the x points between the boundaries. This should return a scheme with a colour cast the same as the foto but with a simple variation. Maybe you'll even get the Apple look.
Just hope you don't get 3 shades of dog puke.
Color quantization is the same process used to choose the palette for low-color GIFs. To get a color palette from a photographic image, I used Nick Rabinowitz’ quantize.js which is based on Leptonica’s MMCQ (modified median cut quantization).
Live web app, about.
The name of the type of algorithm you want is Color Quantization.
Unfortunately I don't have any source code available for you, but I'm sure a google search could turn something up.
In particular, the Dr. Dobb's Journal article on the subject seems promising.
Divide the screen image into a grid of r-many rectangles, in an n by m "grid", each with width (total width / n) and height (total height / m).
1a. Assign a weight to high-profile areas of the screen, such as the left-off-center area.
1b. For each rectangle, assign the pixels into a space of (color,frequency)
For each rectangle R, frequency distribution f_R, and weight W_R:
2a. Determine the i-th scheme color (e.g. i = 1 <--> background color) by scanning the "top frequency", "second frequency" (i.e. f_R[i,:]) for each block.
2b. For each i, put it in a score table, (color_i,score) where score = f_R[i,"frequency"] * W_R
2c. The top scorer for each i will be the i-th scheme color.
Theoretically, if you have a lot of "blue on white" or "red on black", you should get white primary, blue secondary, or black primary, red secondary, for example.
For your text color, either base this directly on a calculation off of background color, or choose secondary color, and if the V difference of HSV is too low, base the color off of the computed scheme color, but augment the V value.
PseudoCode:
float[][] weights =
{ { 1.0, 3.0, 5.0, 5.0, 3.0, 1.0, 1.0, 1.0, 1.0 },
{ 2.0, 6.0, 7.0, 7.0, 6.0, 2.0, 3.0, 3.0, 2.0 },
{ 2.0, 8.0, 9.0, 9.0, 7.0, 3.0, 6.0, 6.0, 3.0 },
{ 2.0, 8.0, 9.0, 9.0, 7.0, 2.0, 3.0, 3.0, 2.0 },
{ 2.0, 7.0, 9.0, 9.0, 7.0, 2.0, 1.0, 1.0, 1.0 },
{ 2.0, 6.0, 7.0, 7.0, 6.0, 2.0, 3.0, 3.0, 1.0 },
{ 1.0, 3.0, 5.0, 5.0, 3.0, 2.0, 6.0, 6.0, 2.0 },
{ 1.0, 1.0, 2.0, 2.0, 1.0, 2.0, 6.0, 6.0, 2.0 },
{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 3.0, 3.0, 1.0 } };
// Leave the following implementations to the imagination:
void DivideImageIntoRegions( Image originalImage, out Image[][] regions );
void GetNthMostCommonColorInRegion( Image region, int n, out Color color );
TKey FindMaximum<TKey, TValue>( Map<TKey, TValue> map );
// The method:
Color[] GetPrimaryScheme( Image image, int ncolors, int M = 9, int N = 9 )
{
Color[] scheme = new Color[ncolors];
Image[][] regions = new Image[M][N];
DivideImageIntoRegions( image, regions );
for( int i = 0; i < ncolors; i++ )
{
Map<Color, float> colorScores = new Map<Color, float>();
for( int m = 0; m < M; m++ )
for( int n = 0; n < N; n++ )
{
Color theColor;
GetNthMostCommonColorInRegion( region, i, theColor );
if( colorScores[theColor] == null )
{ colorScores[theColor] = 0; }
colorScores[theColor] += weights[m][n];
}
scheme[i] = FindMaximum( colorScores );
}
return scheme;
}
Looking at the above, it's clear that if there is a region with little variability, it will have the same second-most-common color as most-common color. To adjust, the second-most-common color in such a case might be null, which one could guard against:
if( theColor != null )
continue;
if( colorScores[theColor] == null )
{ colorScores[theColor] = 0; }
colorScores[theColor] += weights[m][n];
}
Below are some suggestions and discussion around different approaches for generating a color scheme from an image:
First, embed/plot your pixels in some color space. This can be RGB, HSL, or some other color space. Then you can use one of the following to generate a color scheme:
Creating a histogram of the color space - This involves splitting the space into a grid, and counting the pixels in each grid cell. Select the top N cells (histogram buckets) with the most pixels, and average the pixels in each to produce a color per cell. This can be your color scheme.
Median Cut or some other space partitioning technique - This is a nice improvement over #1, as it will split the space by looking at the data.
Clustering Pixels - Cluster the pixels into groups using one of many clustering techniques (k-means, mean-shift, etc.). Then average the pixels in each group to generate a color scheme.
I wrote a more detailed post on the three above approaches here
I also wrote an interactive web app that allows you to load an image an create generate a color palette using one of the above three approaches. You can find the code for it on github