Skip to main content

How to Convert RGB to Color Name in Python

Human-readable color names like "Navy Blue" are more intuitive than RGB tuples (0, 0, 128). Converting between them requires either exact matching against known colors or finding the mathematically closest named color.

This guide covers both approaches with practical implementations.

Exact Match with webcolors

For standard CSS3 colors, use direct lookup:

pip install webcolors
import webcolors

def rgb_to_name_exact(rgb_tuple):
"""Get exact color name or None if no match."""
try:
return webcolors.rgb_to_name(rgb_tuple)
except ValueError:
return None

print(rgb_to_name_exact((255, 0, 0))) # red
print(rgb_to_name_exact((0, 0, 128))) # navy
print(rgb_to_name_exact((250, 5, 5))) # None (no exact match)
Limited Matches

Exact matching only works for the 147 standard CSS3 named colors. Real-world colors from photos or designs rarely match exactly.

Closest Match with Euclidean Distance

Find the nearest named color mathematically:

import webcolors
import math


def closest_color(rgb):
"""Find the closest CSS3 named color."""
min_distance = float('inf')
closest_name = None

# Get all CSS3 color names
for name in webcolors.names("css3"):
r, g, b = webcolors.name_to_rgb(name)

# Euclidean distance
distance = math.sqrt(
(r - rgb[0]) ** 2 +
(g - rgb[1]) ** 2 +
(b - rgb[2]) ** 2
)

if distance < min_distance:
min_distance = distance
closest_name = name

return closest_name


print(closest_color((250, 0, 0))) # red
print(closest_color((250, 10, 10))) # red
print(closest_color((100, 50, 75))) # dimgray (or similar)

Combined Approach

Try exact match first, then fall back to closest:

import webcolors
import math

def rgb_to_name(rgb):
"""Get color name: exact if available, otherwise closest."""
# Try exact match first
try:
return webcolors.rgb_to_name(rgb)
except ValueError:
pass

# Find closest match
min_distance = float('inf')
closest_name = None

for name in webcolors.names("css3"):
r, g, b = webcolors.name_to_rgb(name)
distance = sum((a - b) ** 2 for a, b in zip(rgb, (r, g, b)))

if distance < min_distance:
min_distance = distance
closest_name = name

return closest_name

print(rgb_to_name((255, 0, 0))) # red (exact)
print(rgb_to_name((255, 10, 10))) # red (closest)

Hex Code to Color Name

Common in web development:

import webcolors
import math

def rgb_to_name_with_confidence(rgb):
"""Get color name with distance score."""
# Try exact match
try:
return {
'name': webcolors.rgb_to_name(rgb),
'distance': 0,
'exact_match': True
}
except ValueError:
pass

# Find closest
min_distance = float('inf')
closest_name = None

for name in webcolors.names("css3"):
r, g, b = webcolors.name_to_rgb(name)
distance = math.sqrt(sum((a - b) ** 2 for a, b in zip(rgb, (r, g, b))))

if distance < min_distance:
min_distance = distance
closest_name = name

# Max distance in RGB space is ~441 (0,0,0 to 255,255,255)
confidence = 1 - (min_distance / 441.67)

return {
'name': closest_name,
'distance': round(min_distance, 2),
'confidence': round(confidence, 2),
'exact_match': False
}

print(rgb_to_name_with_confidence((255, 0, 0)))
# {'name': 'red', 'distance': 0, 'exact_match': True}

print(rgb_to_name_with_confidence((200, 50, 50)))
# {'name': 'firebrick', 'distance': 31.56, 'confidence': 0.93, 'exact_match': False}

With Color Distance and Confidence

Return both name and match quality:

import webcolors
import math

def rgb_to_name_with_confidence(rgb):
"""Get color name with distance score."""
# Try exact match
try:
return {
'name': webcolors.rgb_to_name(rgb),
'distance': 0,
'exact_match': True
}
except ValueError:
pass

# Find closest
min_distance = float('inf')
closest_name = None

for name in webcolors.names("css3"):
r, g, b = webcolors.name_to_rgb(name)
distance = math.sqrt(sum((a - b) ** 2 for a, b in zip(rgb, (r, g, b))))

if distance < min_distance:
min_distance = distance
closest_name = name

# Max distance in RGB space is ~441 (0,0,0 to 255,255,255)
confidence = 1 - (min_distance / 441.67)

return {
'name': closest_name,
'distance': round(min_distance, 2),
'confidence': round(confidence, 2),
'exact_match': False
}

print(rgb_to_name_with_confidence((255, 0, 0)))
# {'name': 'red', 'distance': 0, 'exact_match': True}

print(rgb_to_name_with_confidence((200, 50, 50)))
# {'name': 'firebrick', 'distance': 31.56, 'confidence': 0.93, 'exact_match': False}

Color Categories (Simple Grouping)

For basic color classification:

def get_basic_color(rgb):
"""Classify RGB into basic color category."""
r, g, b = rgb

# Check for grayscale
if abs(r - g) < 20 and abs(g - b) < 20 and abs(r - b) < 20:
if max(r, g, b) < 50:
return "black"
elif min(r, g, b) > 200:
return "white"
return "gray"

# Find dominant channel
max_val = max(r, g, b)

if r == max_val:
if g > b:
return "orange" if g > 100 else "red"
return "red" if b < 100 else "pink"
elif g == max_val:
if b > r:
return "cyan" if b > 100 else "green"
return "green" if r < 100 else "yellow"
else: # b == max_val
if r > g:
return "purple" if r > 100 else "blue"
return "blue"

print(get_basic_color((255, 0, 0))) # red
print(get_basic_color((50, 50, 50))) # gray
print(get_basic_color((255, 165, 0))) # orange

Using scipy for Efficient Distance Calculation

For processing many colors:

import webcolors
import numpy as np
from scipy.spatial import KDTree

# Build color lookup once
def build_color_tree():
colors = []
names = []

for name in webcolors.names("css3"):
colors.append(webcolors.name_to_rgb(name))
names.append(name)

return KDTree(colors), names

COLOR_TREE, COLOR_NAMES = build_color_tree()

def rgb_to_name_fast(rgb):
"""Fast color name lookup using KDTree."""
distance, index = COLOR_TREE.query(rgb)
return COLOR_NAMES[index]

# Much faster for bulk processing
print(rgb_to_name_fast((200, 50, 50))) # firebrick

Batch Processing

Convert multiple colors efficiently:

import webcolors
import math

def rgb_to_name(rgb):
"""Get color name: exact if available, otherwise closest."""
# Try exact match first
try:
return webcolors.rgb_to_name(rgb)
except ValueError:
pass

# Find closest match
min_distance = float('inf')
closest_name = None

for name in webcolors.names("css3"):
r, g, b = webcolors.name_to_rgb(name)
distance = sum((a - b) ** 2 for a, b in zip(rgb, (r, g, b)))

if distance < min_distance:
min_distance = distance
closest_name = name

return closest_name

def batch_rgb_to_names(rgb_list):
"""Convert list of RGB tuples to names."""
return [rgb_to_name(rgb) for rgb in rgb_list]

colors = [
(255, 0, 0),
(0, 255, 0),
(0, 0, 255),
(255, 165, 0),
(128, 0, 128)
]

names = batch_rgb_to_names(colors)
print(dict(zip(colors, names)))
# {(255, 0, 0): 'red', (0, 255, 0): 'lime', (0, 0, 255): 'blue', (255, 165, 0): 'orange', (128, 0, 128): 'purple'}

Practical Example: Image Color Analysis

from PIL import Image
from collections import Counter

def get_dominant_colors(image_path, n_colors=5):
"""Get most common color names in an image."""
img = Image.open(image_path)
img = img.convert('RGB')
img = img.resize((100, 100)) # Reduce for speed

pixels = list(img.getdata())

# Convert each pixel to color name
color_names = [rgb_to_name(pixel) for pixel in pixels]

# Count occurrences
return Counter(color_names).most_common(n_colors)

# Usage:
# dominant = get_dominant_colors("photo.jpg")
# print(dominant)
# [('white', 2500), ('silver', 1800), ('gray', 1200), ...]

Summary

InputMethodUse Case
Exact RGBwebcolors.rgb_to_name()Standard CSS colors
Any RGBEuclidean distanceReal-world colors
Hex codehex_to_rgb() + distanceWeb development
Bulk processingKDTree + scipyImage analysis
Best Practice

For user-facing features like image tagging or color pickers, always use the Euclidean distance method. Exact matches are too rare in real-world data (photos, scans) to be useful alone. Consider using LAB color space instead of RGB for perceptually more accurate matches.