color_tools.distance

Color distance metrics for measuring perceptual color differences.

Delta E formulas (LAB space):

  • Delta E 1976 (de76) — Simple Euclidean distance in LAB space; fast but not perceptually uniform.

  • Delta E 1994 (de94) — Adds chroma/hue weighting; better for graphic arts and textiles.

  • Delta E 2000 (de2000) — Current gold standard; most perceptually accurate for small color differences. Use this by default.

  • Delta E CMC (cmc) — Textile industry standard; configurable l:c ratio.

  • HyAB (hyab) — Hybrid metric: absolute L difference + Euclidean chromatic distance. Best for large color differences and image quantization.

Non-LAB metrics:

  • euclidean — Simple Euclidean distance in sRGB space (not perceptually uniform).

  • hsl_euclidean — Euclidean distance in HSL space with circular hue handling.

  • hue_diff_deg — Raw hue angle difference in degrees.

Example

>>> from color_tools import rgb_to_lab, delta_e_2000
>>>
>>> # Compare two similar reds
>>> red1 = rgb_to_lab((255, 0, 0))    # Pure red
>>> red2 = rgb_to_lab((250, 5, 5))    # Slightly darker red
>>>
>>> # Calculate perceptual difference
>>> distance = delta_e_2000(red1, red2)
>>> print(f"ΔE2000: {distance:.2f}")
ΔE2000: 2.85
>>>
>>> # ΔE < 1.0 = imperceptible
>>> # ΔE < 2.0 = barely noticeable
>>> # ΔE < 5.0 = noticeable but acceptable
>>> if distance < 2.0:
...     print("Colors are nearly identical!")
... elif distance < 5.0:
...     print("Colors are similar")
... else:
...     print("Colors are noticeably different")
Colors are similar
color_tools.distance.euclidean(v1, v2)[source]

Simple Euclidean distance between two vectors.

The classic √((x₁-x₂)² + (y₁-y₂)² + (z₁-z₂)²) formula. Works for any dimensionality, not just colors!

Return type:

float

Parameters:
color_tools.distance.hue_diff_deg(h1, h2)[source]

Calculate hue difference accounting for circular nature (0° = 360°).

Hue is circular (like a clock), so the difference between 359° and 1° should be 2°, not 358°! This function finds the smallest angular difference.

Parameters:
  • h1 (float) – Hue angles in degrees

  • h2 (float) – Hue angles in degrees

Return type:

float

Returns:

Smallest hue difference in degrees (0-180)

color_tools.distance.hsl_euclidean(hsl1, hsl2)[source]

Euclidean distance in HSL space (accounting for hue circularity).

Note: HSL distance doesn’t match human perception well - LAB is better! This is here for compatibility with systems that work in HSL.

Return type:

float

Parameters:
color_tools.distance.delta_e_76(lab1, lab2)[source]

Delta E 1976 (CIE76) - Simple Euclidean distance in LAB space.

The OG color difference formula! Just treats LAB like any other 3D space and measures straight-line distance.

Pros: Fast and simple Cons: Doesn’t match human perception well, especially for saturated colors

A ΔE of 1.0 is supposedly the “just noticeable difference” but in reality it varies based on the colors involved.

Parameters:
Return type:

float

Returns:

Delta E 1976 value (lower = more similar)

color_tools.distance.delta_e_94(lab1, lab2, kL=1.0, kC=1.0, kH=1.0, K1=None, K2=None)[source]

Delta E 1994 (CIE94) - Improved perceptual uniformity.

CIE94 realized that humans are more sensitive to lightness differences than chroma differences, and more sensitive to chroma than hue. This formula weights things accordingly!

Better than CIE76 but still has issues with saturated colors.

Parameters:
  • lab1 (Tuple[float, float, float]) – L*a*b* color tuples

  • lab2 (Tuple[float, float, float]) – L*a*b* color tuples

  • kL (float) – Weighting factors (usually all 1.0)

  • kC (float) – Weighting factors (usually all 1.0)

  • kH (float) – Weighting factors (usually all 1.0)

  • K1 (float | None) – Chroma and hue weighting constants (use defaults if None)

  • K2 (float | None) – Chroma and hue weighting constants (use defaults if None)

Return type:

float

Returns:

Delta E 1994 value (lower = more similar)

color_tools.distance.delta_e_2000(lab1, lab2, kL=1.0, kC=1.0, kH=1.0)[source]

Delta E 2000 (CIEDE2000) - Current gold standard for color difference.

This is THE formula to use for color matching! After decades of research, CIEDE2000 finally handles all the edge cases that tripped up earlier formulas: - Neutral colors (low chroma) - Blue hues (which humans perceive differently) - Lightness differences at different brightness levels

The formula is… complex. It’s got rotation terms, weighting functions, and special handling for different hue regions. But it WORKS!

Fun fact: A ΔE2000 of 1.0 is roughly the “just noticeable difference” for most colors. ΔE < 2 is considered imperceptible for most applications.

Parameters:
  • lab1 (Tuple[float, float, float]) – L*a*b* color tuples

  • lab2 (Tuple[float, float, float]) – L*a*b* color tuples

  • kL (float) – Weighting factors (usually all 1.0) - kL for lightness - kC for chroma - kH for hue

  • kC (float) – Weighting factors (usually all 1.0) - kL for lightness - kC for chroma - kH for hue

  • kH (float) – Weighting factors (usually all 1.0) - kL for lightness - kC for chroma - kH for hue

Return type:

float

Returns:

Delta E 2000 value (lower = more similar)

color_tools.distance.delta_e_cmc(lab1, lab2, l=2.0, c=1.0)[source]

Delta E CMC(l:c) - Color difference formula used in textile industry.

CMC was developed specifically for textile color matching, where the context matters (viewing conditions, material properties, etc.).

The l:c ratio lets you tune the formula for different use cases: - CMC(2:1) - “Acceptability” → Used to judge if colors match well enough - CMC(1:1) - “Perceptibility” → More strict, for detecting differences

Why two ratios? Turns out humans are more forgiving of color differences when deciding “is this acceptable?” vs “can I see a difference?”

Parameters:
Return type:

float

Returns:

Delta E CMC value (lower = more similar)

color_tools.distance.delta_e_hyab(lab1, lab2, l_weight=1.0)[source]

HyAB color difference — hybrid absolute/Euclidean distance in LAB space.

Introduced by Abasi, Tehran, and Fairchild (2020) specifically to handle large color differences, where CIE76 and CIEDE2000 are known to produce perceptually inaccurate rankings.

Formula:

d_HyAB = l_weight × |L₁ − L₂|  +  √((a₁ − a₂)² + (b₁ − b₂)²)
         ──────────────────────     ─────────────────────────────
         City-block (lightness)       Euclidean in chroma plane

The name comes from the hybrid of Abs**olute (city-block / Manhattan) distance for lightness and Euclidean distance in the **AB chroma plane. This matches the psychological separability of lightness from hue/chroma: people evaluate brightness shifts and color shifts somewhat independently.

The optional l_weight scales the lightness term — use 1.0 for the pure HyAB formula, or 2.0 (recommended) when using this metric as the distance function in k-means color quantization.

When to use HyAB versus other metrics:

  • CIEDE2000 — best for just-noticeable differences (ΔE < ~5), JND matching, industrial color acceptance, or nearest-neighbor in a small palette.

  • HyAB (l_weight=1.0) — best for large-difference ranking (ΔE > ~20), out-of-gamut mapping, or comparing very different colors.

  • HyAB (l_weight=2.0) — recommended for k-means image quantization. Better lightness separation than CIE76 without CIEDE2000’s per-pixel cost.

Reference:

Abasi, S., Tehran, M. A., & Fairchild, M. D. (2020). Distance metrics for very large color differences. Color Research & Application, 45(2), 208-223. https://doi.org/10.1002/col.22451

Parameters:
  • lab1 (Tuple[float, float, float]) – First color as (L*, a*, b*) tuple.

  • lab2 (Tuple[float, float, float]) – Second color as (L*, a*, b*) tuple.

  • l_weight (float) – Lightness scaling factor. 1.0 = pure HyAB (default); 2.0 = recommended for k-means quantization.

Return type:

float

Returns:

HyAB distance (lower = more similar).

Example

>>> from color_tools import rgb_to_lab, delta_e_hyab
>>>
>>> black = rgb_to_lab((0, 0, 0))
>>> white = rgb_to_lab((255, 255, 255))
>>> delta_e_hyab(black, white)
100.0
>>>
>>> red = rgb_to_lab((255, 0, 0))
>>> blue = rgb_to_lab((0, 0, 255))
>>> delta_e_hyab(red, blue)
...