Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to round an RGB value to the closest web safe value with PIL (Python Imaging Library)?

I'm creating a fast lookup table for colors. Let's say you give me shade of red, then I'll convert it to closest web safe color and perform the lookup. It will return many different shades of red and it's ok for my purposes.

The only problem I have right now is how do I convert a given RGB value to a web safe value? I'm using PIL and Python. I don't mind writing my own algorithm either but it's a bit too difficult for me.

like image 351
bodacydo Avatar asked Oct 24 '12 22:10

bodacydo


2 Answers

I don't know if there is a specific way to do it with PIL, but if you have an rgb colour and we define the closest web safe colour as the one where the distance in the rgb space is the smallest, you can do it as follows:

def getwebsafe(r,g,b):
  rw = 51 * ((int(r)+25)//51)
  gw = 51 * ((int(g)+25)//51)
  bw = 51 * ((int(b)+25)//51)
  return (rw,gw,bw)

This is because web safe colors have 6 possible shades for each component: (0,51,102,153,204,255). If you a component in your colour is in the range [0,25] the closest is 0, if it's in the range [26-76] the closest is 51, etc.

Alternatively, a shorter version for an rgb colour represented as a list:

def getwebsafe(colour):
  return [51*((int(c)+25)//51) for c in colour]

EDIT: Edited to make sure it works even when Python uses non-integer numbers for colours and for Python 2 and 3.

like image 68
pedrosorio Avatar answered Sep 24 '22 19:09

pedrosorio


As I said in a comment under @pedrosorio's answer, I think this could be done more efficently by using table lookups. This means virtually all of the mathematical calculations are done once, when the table is created, and is only done 256 times, rather for each and every pixel. Here's what I mean, using pedrosorio's formula:

tbl = tuple(51*((int(i)+25)//51) for i in xrange(256))

def websafe(*color):
    return tuple(tbl[c] for c in color)

safecolor = websafe(34,55,13)

However, I'm not sure about how the formula maps the 0-255 range values -- because it assigns the following number of entries for each of the six primary shades:

shade count
 0x00  26
 0x33  51
 0x66  51
 0x99  51
 0xcc  51
 0xff  26

Instead, I would use this slightly simpler formula:

tbl = tuple(51*(i//43) for i in xrange(256))

which gives a more even distribution of the shades:

shade count
 0x00  43
 0x33  43
 0x66  43
 0x99  43
 0xcc  43
 0xff  41

Once you know the distribution count you could even do something like this which involves very little math. Of course there's little reason to bother optimizing non-speed-crucial code.

tbl = ((0x00,)*43 + (0x33,)*43 + (0x66,)*43 +
       (0x99,)*43 + (0xcc,)*43 + (0xff,)*41)
like image 25
martineau Avatar answered Sep 22 '22 19:09

martineau