MikeVaughan
MikeVaughan

Reputation: 1501

Convert RGB color to English color name, like 'green' with Python

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?

Upvotes: 80

Views: 77618

Answers (8)

fraxel
fraxel

Reputation: 35319

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):
    distances = {}
    for name in webcolors.names():
        r_c, g_c, b_c = webcolors.name_to_rgb(name)
        rd = (r_c - requested_colour[0]) ** 2
        gd = (g_c - requested_colour[1]) ** 2
        bd = (b_c - requested_colour[2]) ** 2
        distances[name] = rd + gd + bd
    return min(distances, key=distances.get)

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

Upvotes: 128

Jason Sundram
Jason Sundram

Reputation: 12574

@fraxel's answer above is really good. However, matplotlib.colors contains XKCD color names which is a longer list (949) than the CSS4 colors (148), which gives you a better chance of getting a better name for your color. Taking a similar approach (minimizing euclidean distance in RGB space) with the different set of colors:

import numpy as np
from matplotlib.colors import hex2color, XKCD_COLORS


class ColorNames:
    def __init__(self, color_dict):
        """color_dict: A map of names to hex values."""
        self.colors = np.array([hex2color(c) for c in color_dict.values()])
        self.names = list(color_dict.keys())

    def closest(self, color):
        v = hex2color(color)
        distances = np.sum((self.colors - v) ** 2, axis=1)
        closest_index = np.argmin(distances)
        return self.names[closest_index]

# Usage:
# Optional: Trim the 'xkcd:' prefix from the name for more readable results.
xkcd = {n.replace('xkcd:', ''): c for n, c in XKCD_COLORS.items()}
names = ColorNames(xkcd)

# Arbitrary list of test colors
colors = ["#30c5ff", "#2a2d34", "#5c946e", "#80c2af", "#a0dde6"]
for c in colors:
    print(f"The best name for {c} is: {names.closest(c)}")

Note that matplotlib.colors also contains CSS4 Colors, so you could also do the following:

from matplotlib.colors import CSS4_COLORS

names = ColorNames(CSS4_COLORS)
for c in colors:
    print(f"The best name for {c} is: {names.closest(c)}")

Here are the colors so you can judge the name quality for yourself. Hex Code, Closest Names, and Colors Represented

The code to reproduce this graphic is on github.

Upvotes: 2

Marc Compere
Marc Compere

Reputation: 343

June 2024 update

as of June 2024, the maintainer of webcolors removed the list of colors from the public interface. the last bullet on the changelog for v24.6.0 explains this.

following the pattern of excellent answers above from both @fraxel and @fiatjaf, this updated version replaces the list of color names to something that is defined locally.

the complete working example, starting with kml colors, is here for python 3:

import webcolors

def get_colour_name(rgb_triplet):
    # full list: https://www.w3schools.com/tags/ref_colornames.asp
    myColors = {
        "red"     : "#ff0000", # R
        "orange"  : "#ffa500", # O
        "yellow"  : "#ffff00", # Y
        "green"   : "#008000", # G
        "blue"    : "#0000ff", # B
        "magenta" : "#ff00ff", # I
        "purple"  : "#800080", # V
        
        "coral"   : "#ff7f50", # light red
        "maroon"  : "#800000", # dark red
        "navy"    : "#000080", # dark blue
        "cyan"    : "#00ffff", # light blue
        "gold"    : "#ffd700", # dark yellow
        "lime"    : "#00ff00", # bright green
        "jade"    : "#00a36c", # light green
        "olive"   : "#808000", # dark green
        "grey"    : "#808080" }
        
    min_colours = {}
    for name, hex_val in myColors.items():
        r_c, g_c, b_c = webcolors.hex_to_rgb(hex_val)
        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())]


# what is the name for this kml color???
kml_color='ff00eb00' # this is kml for green

# kml colors are non-standard: aabbggrr    (alpha, blue, green, red)
def kml_color_to_rgba(kml_color):
    # convert kml color to standard RGB's and alpha
    alpha=int( kml_color[0:2] , 16)
    blu  =int( kml_color[2:4] , 16) # convert 2-place hex into 16-bit int
    grn  =int( kml_color[4:6] , 16)
    red  =int( kml_color[6:]  , 16)
    print(f'kml_color={kml_color} --> RGB=({red},{grn},{blu}), alpha={alpha}')
    return red,grn,blu,alpha


rgba = kml_color_to_rgba(kml_color)

closest_colorname = get_colour_name(rgba)
print(f'the closest CSS color to {kml_color}, which is RGBA={rgba} is {closest_colorname}')

Upvotes: 1

JSch9619
JSch9619

Reputation: 31

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

Upvotes: 3

CutMaster Prod
CutMaster Prod

Reputation: 86

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.

Upvotes: 2

fiatjaf
fiatjaf

Reputation: 12187

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())]

Upvotes: 13

Alceu Costa
Alceu Costa

Reputation: 9899

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.

Upvotes: 3

ChapMic
ChapMic

Reputation: 28154

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

Upvotes: 12

Related Questions