Generate color palette from image with ImageMagick

13.2k views Asked by At

I need to generate a color palette of the top 5 dominant colors in an image. I'd like to replicate the results of Embedly's extract tool or Color Thief.

The following command gives me slightly different results:

convert testimage.jpg  -format %c -colorspace LAB -colors 5 histogram:info:- | sort -n -r
    157154: ( 19, 28, 35) #131C23 srgb(19,28,35)
     16164: ( 27, 51, 77) #1B334D srgb(27,51,77)
     15725: ( 79, 88, 84) #4F5854 srgb(79,88,84)
      8608: ( 44, 77,103) #2C4D67 srgb(44,77,103)
      5149: ( 84,126,150) #547E96 srgb(84,126,150)

I'm a bit unsure if I should quantize to 5 colors as I've found that doing so doesn't work so well with simple graphics (for example the Google logo). Is it better to use a larger color palette and then just select the top n colors?

This leads me on to my next question regarding the quantization algorithm used. Looking at the results of Embedly Extract, the output colors are not necessarily the most frequent but appear to be the clusters that are the most different from each other.

For example suppose I have a very dark image (black/browns) with a small detail in bright red. How would I ensure that ImageMagick includes the red? (apologies if this sounds dumb, color theory is all new to me!).

Below is the image I've been using for testing:

enter image description here

2

There are 2 answers

5
Kurt Pfeifle On

Can you define "top 5 dominant colors", please? I think this isn't as easy as it sounds...

This is clearly shown by the different results which can be seen when visiting the links you provided for Embed.ly's and for Color Thief's interpretation of your test image.

Embed.ly

Here is what Embed.ly lists as its 5 extracted colors (I looked at the HTML source code of the page to find out):

 rgb(13, 28, 37)
 rgb(44, 74, 94)
 rgb(71, 112, 131)
 rgb(105, 147, 163
 rgb(198, 209, 216)

Use ImageMagick to create a color palette with these 5 colors:

 convert                        \    
     -size 60x60                \    
      label:"        Embed.ly"  \
      xc:"rgb(13, 28, 37)"      \    
      xc:"rgb(105, 147, 163"    \    
      xc:"rgb(71, 112, 131)"    \    
      xc:"rgb(44, 74, 94)"      \    
      xc:"rgb(198, 209, 216)"   \
     +append                    \    
      embedly-palette-from-testimage.jpg

Look at the result:

Embed.ly's pick of 5 colors...

Color Thief

Color Thief names one color as the "dominant" color:

 rgb(21, 30, 38)

Color Thief also lists a palette of 9 more colors (again, values retrieved from HTML source code):

 rgb(18, 27, 35)
 rgb(100, 142, 164) 
 rgb(51, 84, 110) 
 rgb(32, 53, 74)
 rgb(47, 46, 43)
 rgb(83, 85, 76)
 rgb(145, 143, 128) 
 rgb(106, 141, 140) 
 rgb(62, 84, 81)

Use ImageMagick to create a color palette with Color Thief's 9 pallete colors:

 convert                        \    
     -size 60x60                \    
      label:"     Color Thief"  \
      xc:"rgb(18, 27, 35)"      \    
      xc:"rgb(100, 142, 164)"   \
      xc:"rgb(51, 84, 110)"     \    
      xc:"rgb(32, 53, 74)"      \    
      xc:"rgb(47, 46, 43)"      \    
      xc:"rgb(83, 85, 76)"      \    
      xc:"rgb(145, 143, 128)"   \
      xc:"rgb(106, 141, 140)"   \
      xc:"rgb(62, 84, 81)"      \    
     +append                    \    
      ct-palette-from-testimage.jpg

Look at the result:

Color Thief's palette of 9 extracted colors...

Color Thief is based on quantize.js. It uses the median cut algorithm provided by quantize.js to cluster similar colors and then returns the base color from the largest cluster as the "dominant" color.

How it determines which colors to return as "palette colors" can be determined from its source code, which is hosted on Github.

ImageMagick's 5 quantized colors

Your question lists the output of ImageMagick's histogram after quantizing the image to 5 colors only.

Use these 5 colors to create another color palette:

 convert                        \
     -size 60x60                \
      label:"   ImageMagick"    \
      xc:"srgb(19,28,35)"       \
      xc:"srgb(79,88,84)"       \
      xc:"srgb(44,77,103)"      \
      xc:"srgb(27,51,77)"       \
      xc:"srgb(84,126,150)"     \
     +append                    \
      im-palette-from-testimage.jpg

Look at the result:

ImageMagick's 5 quantized colors...

Compare 3 color palettes

Use this command to create a visual comparison of the 3 color palettes:

 convert                                    \
      ct-palette-from-testimage.jpg         \
      embedly-palette-from-testimage.jpg    \
      im-palette-from-testimage.jpg         \
     -append                                \
      color-palettes.jpg

Result:

Direct visual comparison of 3 created color palettes...

As can clearly be seen, neither Color Thief nor the 5 quantized colors from ImageMagick's histogram do include the rather bright 5th color returned by Embed.ly.

Compare again to your test image:

Test image...

"Is it better to use a larger color palette and then just select the top n colors?"

Why don't you test it and find out yourself?

1
Klaatu von Schlacker On

This is a bit of a hack, but here's what I do:

  • scale the image down to 8x8 or 16x16,
  • limit colour depth to 16 or 24 or 32
  • select unique colours from the scaled image and dump them into a file

This forces Image Magick to create a basic sample of colours, and then selects those and places them into a colour swatch array.

Using Ben's example photo, here's the command and results:

convert image.png -geometry 16x16 -colors 32 \
-unique-colors -scale 4000% scheme.png

imagemagick