4

I am trying to create an API which takes an image URL as input and returns back a color palette in JSON format as output.

It should work something like this: http://lokeshdhakar.com/projects/color-thief/

But should be in Python. I have looked into PIL (Python Image Library) but didn't get what I want. Can someone point me in the right direction?

Input: Image URL
Output: List of Colors as a palette
Jyotiska
  • 255
  • 3
  • 15

2 Answers2

14
import numpy as np
import Image

def palette(img):
    """
    Return palette in descending order of frequency
    """
    arr = np.asarray(img)
    palette, index = np.unique(asvoid(arr).ravel(), return_inverse=True)
    palette = palette.view(arr.dtype).reshape(-1, arr.shape[-1])
    count = np.bincount(index)
    order = np.argsort(count)
    return palette[order[::-1]]

def asvoid(arr):
    """View the array as dtype np.void (bytes)
    This collapses ND-arrays to 1D-arrays, so you can perform 1D operations on them.
    http://stackoverflow.com/a/16216866/190597 (Jaime)
    http://stackoverflow.com/a/16840350/190597 (Jaime)
    Warning:
    >>> asvoid([-0.]) == asvoid([0.])
    array([False], dtype=bool)
    """
    arr = np.ascontiguousarray(arr)
    return arr.view(np.dtype((np.void, arr.dtype.itemsize * arr.shape[-1])))


img = Image.open(FILENAME, 'r').convert('RGB')
print(palette(img))

palette(img) returns a numpy array. Each row can be interpreted as a color:

[[255 255 255]
 [  0   0   0]
 [254 254 254]
 ..., 
 [213 213 167]
 [213 213 169]
 [199 131  43]]

To get the top ten colors:

palette(img)[:10]
unutbu
  • 842,883
  • 184
  • 1,785
  • 1,677
  • Well, I want to get to get top 10 dominating colors. How do I retrieve that from the output? – Jyotiska Sep 14 '13 at 11:40
  • @ManishPatel: Take a look at http://stackoverflow.com/questions/9448029/print-an-integer-array-as-hexadecimal-numbers. If that does not answer your question, please post a new question with a sample of your input (is it an array, a list, a int?) and the expected output. – unutbu Feb 17 '15 at 11:15
  • This is excelent and suepr fast. Can you suggest a way to convert it into an RGBa image? You now have a palette of shape (nbcolors,4) how do I make so that is (nbcolors,1,4) to have an image of size nbcolors x 1 pixels and 4 channels? – lesolorzanov May 16 '18 at 14:21
  • 1
    @ZloySmiertniy: Given `p = palette(img)` with shape `(nbcolors, 4)`, you could use `p[:,None,:]` or [`p[:,np.newaxis,:]`](https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html), or [`p.reshape(-1, 1, 4)`](https://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.reshape.html) to obtain an array of shape `(nbcolors, 1, 4)`. (`reshape` will automatically replace the minus 1 with the only reasonable choice for you). – unutbu May 16 '18 at 16:33
  • @unutbu thank you, I was using np.expand_dims(p, axis=0) It is good to see many possible approaches – lesolorzanov May 16 '18 at 17:53
3

The color-thief library is also available in python: https://github.com/fengsp/color-thief-py

Example implementation :

pip install colorthief
# -*- coding: utf-8 -*-

import sys

if sys.version_info < (3, 0):
    from urllib2 import urlopen
else:
    from urllib.request import urlopen

import io

from colorthief import ColorThief


fd = urlopen('http://lokeshdhakar.com/projects/color-thief/img/photo1.jpg')
f = io.BytesIO(fd.read())
color_thief = ColorThief(f)
print(color_thief.get_color(quality=1))
print(color_thief.get_palette(quality=1))
Darshan Gada
  • 583
  • 5
  • 9