Color Distance

Color distance (Delta E) is a metric for measuring the perceived difference between two colors. Colorium implements three industry-standard algorithms for color distance calculation.

Overview of Distance Methods

MethodDescriptionBest For
CIE76Simple Euclidean distance in LAB spaceQuick estimates
CIE94Improved with weighting factorsGraphic arts
CIEDE2000Industry standard with perceptual correctionsAccurate color matching

Understanding Delta E

Delta E values indicate how different two colors appear:

Delta E RangePerceptual Difference
0 - 1Imperceptible
1 - 2Barely perceptible
2 - 10Perceptible
10 - 20Noticeable
20 - 50Very different
50+Completely different

CIE76 Distance

The simplest Delta E method using Euclidean distance in LAB space.

1
2
3
4
5
6
7
8
from colorium import Color

color1 = Color(100, 150, 200)
color2 = Color(120, 130, 180)

# Calculate CIE76 distance
distance = color1.delta_e(color2, "cie76")
print(f"CIE76: {distance:.2f}")

When to Use CIE76

  • Quick estimates
  • Simple color difference needs
  • Legacy systems
  • When accuracy is not critical

CIE94 Distance

Improves upon CIE76 with weighting factors.

1
2
3
4
5
6
7
8
from colorium import Color

color1 = Color(100, 150, 200)
color2 = Color(120, 130, 180)

# Calculate CIE94 distance
distance = color1.delta_e(color2, "cie94")
print(f"CIE94: {distance:.2f}")

When to Use CIE94

  • Graphic arts applications
  • Textile color matching
  • When CIE76 is insufficient
  • When CIEDE2000 is too complex

CIEDE2000 Distance

The most accurate Delta E method, industry standard.

1
2
3
4
5
6
7
8
from colorium import Color

color1 = Color(100, 150, 200)
color2 = Color(120, 130, 180)

# Calculate CIEDE2000 distance
distance = color1.delta_e(color2, "cie2000")
print(f"CIE2000: {distance:.2f}")

When to Use CIEDE2000

  • Industry color matching
  • Quality control
  • Color science research
  • When maximum accuracy is required

Comparing Methods

1
2
3
4
5
6
7
8
9
10
11
12
13
from colorium import Color

color1 = Color(255, 0, 0)    # Red
color2 = Color(0, 0, 255)    # Blue

# Compare all methods
cie76 = color1.delta_e(color2, "cie76")
cie94 = color1.delta_e(color2, "cie94")
cie2000 = color1.delta_e(color2, "cie2000")

print(f"CIE76: {cie76:.2f}")
print(f"CIE94: {cie94:.2f}")
print(f"CIE2000: {cie2000:.2f}")

Practical Examples

Color Matching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from colorium import Color

def find_closest_color(target, palette):
    """Find the closest color in a palette"""
    closest = None
    min_distance = float('inf')
    
    for color in palette:
        distance = target.delta_e(color, "cie2000")
        if distance < min_distance:
            min_distance = distance
            closest = color
    
    return closest, min_distance

# Sample palette
palette = [
    Color(255, 0, 0),     # Red
    Color(0, 255, 0),     # Green
    Color(0, 0, 255),     # Blue
    Color(255, 255, 0),   # Yellow
    Color(255, 0, 255),   # Magenta
    Color(0, 255, 255)    # Cyan
]

# Find closest color
target = Color(200, 50, 50)
closest, distance = find_closest_color(target, palette)

print(f"Target: {target.to_hex_string()}")
print(f"Closest: {closest.to_hex_string()}")
print(f"Distance: {distance:.2f}")

Color Quality Control

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
from colorium import Color

class ColorQualityControl:
    def __init__(self, tolerance=2.0):
        self.tolerance = tolerance
        self.results = []
    
    def check_color(self, sample, reference):
        """Check if color matches reference within tolerance"""
        distance = sample.delta_e(reference, "cie2000")
        passed = distance <= self.tolerance
        
        self.results.append({
            'sample': sample,
            'reference': reference,
            'distance': distance,
            'passed': passed
        })
        
        return passed
    
    def get_summary(self):
        """Get quality control summary"""
        total = len(self.results)
        passed = sum(1 for r in self.results if r['passed'])
        
        return {
            'total': total,
            'passed': passed,
            'failed': total - passed,
            'pass_rate': passed / total if total > 0 else 0
        }

# Usage
qc = ColorQualityControl(tolerance=3.0)

reference = Color(255, 0, 0)
samples = [
    Color(254, 0, 0),
    Color(255, 5, 0),
    Color(240, 0, 0),
    Color(200, 50, 50)
]

for sample in samples:
    passed = qc.check_color(sample, reference)
    print(f"{sample.to_hex_string()}{'✓' if passed else '✗'}")

summary = qc.get_summary()
print(f"\nPass rate: {summary['pass_rate']:.0%}")

Color Difference Visualization

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from colorium import Color

def visualize_color_difference(color1, color2):
    """Visualize the difference between two colors"""
    distances = {
        'CIE76': color1.delta_e(color2, "cie76"),
        'CIE94': color1.delta_e(color2, "cie94"),
        'CIE2000': color1.delta_e(color2, "cie2000")
    }
    
    # Determine perception level
    d = distances['CIE2000']
    if d < 1:
        perception = "Imperceptible"
    elif d < 2:
        perception = "Barely perceptible"
    elif d < 10:
        perception = "Perceptible"
    elif d < 20:
        perception = "Noticeable"
    elif d < 50:
        perception = "Very different"
    else:
        perception = "Completely different"
    
    print(f"Color 1: {color1.to_hex_string()}")
    print(f"Color 2: {color2.to_hex_string()}")
    print(f"Perception: {perception}")
    print(f"Delta E (CIE2000): {distances['CIE2000']:.2f}")
    print(f"CIE76: {distances['CIE76']:.2f}")
    print(f"CIE94: {distances['CIE94']:.2f}")

# Example
red = Color(255, 0, 0)
orange = Color(255, 128, 0)
visualize_color_difference(red, orange)

Distance Tolerance Guidelines

For Different Applications

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from colorium import Color

def get_tolerance(application):
    """Get recommended tolerance for different applications"""
    tolerances = {
        'paint_matching': 1.0,
        'textile_matching': 2.0,
        'print_quality': 3.0,
        'display_calibration': 1.5,
        'color_grading': 2.5,
        'general_use': 5.0
    }
    return tolerances.get(application, 5.0)

# Example
tolerance = get_tolerance('paint_matching')
print(f"Recommended tolerance: {tolerance}")

Tolerance Classes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
from colorium import Color

class ColorTolerance:
    TIGHT = 1.0
    MEDIUM = 2.0
    LOOSE = 5.0
    VERY_LOOSE = 10.0
    
    @staticmethod
    def get_description(tolerance):
        descriptions = {
            1.0: "Tight (precise matching)",
            2.0: "Medium (standard quality)",
            5.0: "Loose (general use)",
            10.0: "Very loose (approximate)"
        }
        return descriptions.get(tolerance, "Custom")

# Usage
color1 = Color(100, 150, 200)
color2 = Color(105, 145, 195)

distance = color1.delta_e(color2, "cie2000")
tolerance = ColorTolerance.MEDIUM

print(f"Distance: {distance:.2f}")
print(f"Tolerance: {ColorTolerance.get_description(tolerance)}")
print(f"Pass: {distance <= tolerance}")

Advanced Topics

Color Difference in Different Spaces

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
from colorium import Color

def compare_in_spaces(color1, color2):
    """Compare colors in different spaces"""
    # RGB difference
    rgb_diff = (
        abs(color1.red - color2.red) +
        abs(color1.green - color2.green) +
        abs(color1.blue - color2.blue)
    ) / 3
    
    # HSL difference
    hsl1 = color1.to_hsl()
    hsl2 = color2.to_hsl()
    hsl_diff = (
        abs(hsl1['h'] - hsl2['h']) / 360 +
        abs(hsl1['s'] - hsl2['s']) +
        abs(hsl1['l'] - hsl2['l'])
    ) / 3 * 100
    
    # CIE2000 difference
    cie_diff = color1.delta_e(color2, "cie2000")
    
    return {
        'rgb': rgb_diff,
        'hsl': hsl_diff,
        'cie2000': cie_diff
    }

# Example
red = Color(255, 0, 0)
pink = Color(255, 200, 200)
results = compare_in_spaces(red, pink)

print("Color differences:")
print(f"RGB: {results['rgb']:.2f}")
print(f"HSL: {results['hsl']:.2f}")
print(f"CIE2000: {results['cie2000']:.2f}")

Batch Distance Calculation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from colorium import Color

def batch_distance(target, colors):
    """Calculate distance to multiple colors"""
    return [
        {
            'color': color,
            'distance': target.delta_e(color, "cie2000")
        }
        for color in colors
    ]

# Sort by distance
target = Color(100, 150, 200)
colors = [
    Color(120, 130, 180),
    Color(255, 0, 0),
    Color(100, 150, 200),
    Color(0, 255, 0)
]

distances = batch_distance(target, colors)
sorted_distances = sorted(distances, key=lambda x: x['distance'])

for item in sorted_distances:
    print(f"{item['color'].to_hex_string()}: {item['distance']:.2f}")

Best Practices

Choose the Right Method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from colorium import Color

def get_distance(color1, color2, accuracy="medium"):
    """Get distance with appropriate accuracy"""
    methods = {
        'quick': 'cie76',
        'medium': 'cie94',
        'accurate': 'cie2000'
    }
    
    method = methods.get(accuracy, 'cie2000')
    return color1.delta_e(color2, method)

# Usage
distance = get_distance(
    Color(100, 150, 200),
    Color(120, 130, 180),
    accuracy="accurate"
)

Cache Distance Results

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
from colorium import Color
from functools import lru_cache

class ColorDistanceCache:
    def __init__(self):
        self.cache = {}
    
    def get_distance(self, color1, color2, method="cie2000"):
        # Create cache key
        key = (
            (color1.red, color1.green, color1.blue),
            (color2.red, color2.green, color2.blue),
            method
        )
        
        if key not in self.cache:
            self.cache[key] = color1.delta_e(color2, method)
        
        return self.cache[key]

# Usage
cache = ColorDistanceCache()
color1 = Color(100, 150, 200)
color2 = Color(120, 130, 180)

# First call calculates
distance1 = cache.get_distance(color1, color2)

# Second call uses cache
distance2 = cache.get_distance(color1, color2)

Next Steps


Previous: Color Blending Next: Color Similarity →

On this page
23 sections