75

I want to convert a color tuple to a color name, like 'yellow' or 'blue'

>>> im = Image.open("test.jpg")
>>> n, color = max(im.getcolors(im.size[0]*im.size[1]))
>>> print color
(119, 172, 152)

Is there a simple way in python to do this?

Ripon Al Wasim
  • 36,924
  • 42
  • 155
  • 176
MikeVaughan
  • 1,441
  • 1
  • 11
  • 18
  • 2
    I think I found the droids you're looking for... http://pypi.python.org/pypi/webcolors/1.3#conversions-from-integer-rgb-triplets – mVChr Mar 14 '12 at 00:19
  • 1
    I don't know if there's already a solution to this, but any such mapping will necessarily be heuristic and entirely subjective... Interesting question, though! – Will Vousden Mar 14 '12 at 00:21
  • 2
    Do you want _precise_ colour names or approximations? For example, "green" is (obviously) `(0,255,0)`. What would you like `color((0,254,0))` to be? – Katriel Mar 14 '12 at 00:25
  • 1
    @WillVousden not necessarily: some colours have official names. – Katriel Mar 14 '12 at 00:25
  • Do you want the colors to be the same colors that are used when setting a font color in HTML (e.g., `Sample text`)? – Anderson Green Jun 10 '13 at 14:58

6 Answers6

119

It looks like webcolors will allow you to do this:

rgb_to_name(rgb_triplet, spec='css3')

Convert a 3-tuple of integers, suitable for use in an rgb() color triplet, to its corresponding normalized color name, if any such name exists; valid values are html4, css2, css21 and css3, and the default is css3.

Example:

>>> rgb_to_name((0, 0, 0))
'black'

it is vice-versa-able:

>>> name_to_rgb('navy')
(0, 0, 128)

#To find the closest colour name:

However webcolors raises an exception if it can't find a match for the requested colour. I've written a little fix that delivers the closest matching name for the requested RGB colour. It matches by Euclidian distance in the RGB space.

import webcolors

def closest_colour(requested_colour):
    min_colours = {}
    for key, name in webcolors.CSS3_HEX_TO_NAMES.items():
        r_c, g_c, b_c = webcolors.hex_to_rgb(key)
        rd = (r_c - requested_colour[0]) ** 2
        gd = (g_c - requested_colour[1]) ** 2
        bd = (b_c - requested_colour[2]) ** 2
        min_colours[(rd + gd + bd)] = name
    return min_colours[min(min_colours.keys())]

def get_colour_name(requested_colour):
    try:
        closest_name = actual_name = webcolors.rgb_to_name(requested_colour)
    except ValueError:
        closest_name = closest_colour(requested_colour)
        actual_name = None
    return actual_name, closest_name

requested_colour = (119, 172, 152)
actual_name, closest_name = get_colour_name(requested_colour)

print("Actual colour name:", actual_name, ", closest colour name:", closest_name)

Output:

Actual colour name: None , closest colour name: cadetblue
Prakash Dahal
  • 4,388
  • 2
  • 11
  • 25
fraxel
  • 34,470
  • 11
  • 98
  • 102
  • 2
    It doesn't work for all RGB combinations like: print(webcolors.rgb_to_name((231, 201, 186))) throws error i.e. "ValueError: '#e7c9ba' has no defined color name in css3" – Asim Apr 12 '20 at 13:06
  • 4
    @Asim, it looks like this code is for an earlier (probably Py2) version. For webcolors-1.11.1, you need to replace ```for key, name in webcolors.css3_hex_to_names.items():``` with ```for name, key in webcolors.CSS3_HEX_TO_NAMES.items():```. Then the rest of the code (aside from the print statement which just needs to be f-stringed and wrapped in parentheses) should work. – Andrew Stewart Nov 17 '20 at 23:26
  • 5
    @AndrewStewart `for key, name in webcolors.CSS3_HEX_TO_NAMES.items():` worked for me with webcolors-1.11.1. I switched `key` and `name`. – Rajab Shakirov Feb 08 '21 at 03:18
12

For those who, like me, want a more familiar colour name, you can use the CSS 2.1 colour names, also provided by webcolors:

  • aqua: #00ffff
  • black: #000000
  • blue: #0000ff
  • fuchsia: #ff00ff
  • green: #008000
  • grey: #808080
  • lime: #00ff00
  • maroon: #800000
  • navy: #000080
  • olive: #808000
  • purple: #800080
  • red: #ff0000
  • silver: #c0c0c0
  • teal: #008080
  • white: #ffffff
  • yellow: #ffff00
  • orange: #ffa500

Just use fraxel's excellent answer and code for getting the closest colour, adapted to CSS 2.1:

def get_colour_name(rgb_triplet):
    min_colours = {}
    for key, name in webcolors.css21_hex_to_names.items():
        r_c, g_c, b_c = webcolors.hex_to_rgb(key)
        rd = (r_c - rgb_triplet[0]) ** 2
        gd = (g_c - rgb_triplet[1]) ** 2
        bd = (b_c - rgb_triplet[2]) ** 2
        min_colours[(rd + gd + bd)] = name
    return min_colours[min(min_colours.keys())]
fiatjaf
  • 11,479
  • 5
  • 56
  • 72
  • 4
    You can have a greater accuracy using the CSS4 list, which is provided by `matplotlib.colors`. For example `import matplotlib.colors as mc` and `mycss4list = mc.CSS4_COLORS` – loved.by.Jesus Jan 22 '20 at 16:30
  • I had to use the constant CSS21_HEX_TO_NAMES (uppercase) to get this to work. – John Hunt Jul 19 '22 at 12:19
12

There is a program called pynche which can change RGB to colour name in English for Python.

You can try to use the method ColorDB.nearest() in ColorDB.py which can do what you want.

You can find more information about this method here : ColorDB Pynche

ChapMic
  • 26,954
  • 1
  • 21
  • 20
  • 1
    Provided link doesn't work anymore. Here you can find file [ColorDB.py](https://github.com/python/cpython/blob/master/Tools/pynche/ColorDB.py). – ands Jan 16 '19 at 20:08
  • For anyone in the future viewing this post, here is a modified version of ColorDB.py I modified for python3: https://github.com/EricPanDev/python3-colordb/blob/main/ColorDB.py – Eric Apr 22 '23 at 06:05
3

A solution to your problem consists in mapping the RGB values to the HSL color space.

Once you have the color in the HSL color space you can use the H (hue) component to map it the color. Note that color is a somewhat subjective concept, so you would have to define which ranges of H corresponds to a given color.

Alceu Costa
  • 9,733
  • 19
  • 65
  • 83
2

The best solution I've found to solve this problem is the one provided by tux21b on this post:
find the colour name from a hexadecimal colour code
With the fix I've made (to avoid the division by zero error).
It is (from my understanding) an approximation calculation, that doesn't require to load a large set of data table to get an approaching value, and that allow to set your own set of needed colors.

1

I would just use a dictionary to figure out the base colors, and then find the closest one.:

def get_color_name(rgb):
colors = {
    "red": (255, 0, 0),
    "green": (0, 255, 0),
    "blue": (0, 0, 255),
    "yellow": (255, 255, 0),
    "magenta": (255, 0, 255),
    "cyan": (0, 255, 255),
    "black": (0, 0, 0),
    "white": (255, 255, 255)
}
min_distance = float("inf")
closest_color = None
for color, value in colors.items():
    distance = sum([(i - j) ** 2 for i, j in zip(rgb, value)])
    if distance < min_distance:
        min_distance = distance
        closest_color = color
return closest_color

# Testing
print(get_color_name((255, 0, 0)))  # red
print(get_color_name((0, 255, 0)))  # green
print(get_color_name((0, 0, 255)))  # blue
JSch9619
  • 11
  • 1