Source code for blessed.color

# -*- coding: utf-8 -*-
"""
Sub-module providing color functions.

References,

- https://en.wikipedia.org/wiki/Color_difference
- http://www.easyrgb.com/en/math.php
- Measuring Colour by R.W.G. Hunt and M.R. Pointer
"""

# std imports
from math import cos, exp, sin, sqrt, atan2

# isort: off
try:
    from functools import lru_cache
except ImportError:
    # lru_cache was added in Python 3.2
    from backports.functools_lru_cache import lru_cache


[docs] def rgb_to_xyz(red, green, blue): """ Convert standard RGB color to XYZ color. D65/2° standard illuminant. :arg int red: RGB value of Red. :arg int green: RGB value of Green. :arg int blue: RGB value of Blue. :returns: Tuple (X, Y, Z) representing XYZ color :rtype: tuple """ rgb = [] for val in red, green, blue: val /= 255.0 if val > 0.04045: val = pow((val + 0.055) / 1.055, 2.4) else: val /= 12.92 val *= 100 rgb.append(val) red, green, blue = rgb # pylint: disable=unbalanced-tuple-unpacking x_val = red * 0.4124 + green * 0.3576 + blue * 0.1805 y_val = red * 0.2126 + green * 0.7152 + blue * 0.0722 z_val = red * 0.0193 + green * 0.1192 + blue * 0.9505 return x_val, y_val, z_val
[docs] def xyz_to_lab(x_val, y_val, z_val): """ Convert XYZ color to CIE-Lab color. :arg float x_val: XYZ value of X. :arg float y_val: XYZ value of Y. :arg float z_val: XYZ value of Z. :returns: Tuple (L, a, b) representing CIE-Lab color :rtype: tuple D65/2° standard illuminant """ xyz = [] for val, ref in (x_val, 95.047), (y_val, 100.0), (z_val, 108.883): val /= ref val = pow(val, 1 / 3.0) if val > 0.008856 else 7.787 * val + 16 / 116.0 xyz.append(val) x_val, y_val, z_val = xyz # pylint: disable=unbalanced-tuple-unpacking cie_l = 116 * y_val - 16 cie_a = 500 * (x_val - y_val) cie_b = 200 * (y_val - z_val) return cie_l, cie_a, cie_b
[docs] @lru_cache(maxsize=256) def rgb_to_lab(red, green, blue): """ Convert RGB color to CIE-Lab color. :arg int red: RGB value of Red. :arg int green: RGB value of Green. :arg int blue: RGB value of Blue. :returns: Tuple (L, a, b) representing CIE-Lab color :rtype: tuple D65/2° standard illuminant """ return xyz_to_lab(*rgb_to_xyz(red, green, blue))
[docs] def dist_rgb(rgb1, rgb2): """ Determine distance between two rgb colors. :arg tuple rgb1: RGB color definition :arg tuple rgb2: RGB color definition :returns: Square of the distance between provided colors :rtype: float This works by treating RGB colors as coordinates in three dimensional space and finding the closest point within the configured color range using the formula:: d^2 = (r2 - r1)^2 + (g2 - g1)^2 + (b2 - b1)^2 For efficiency, the square of the distance is returned which is sufficient for comparisons """ return sum(pow(rgb1[idx] - rgb2[idx], 2) for idx in (0, 1, 2))
[docs] def dist_rgb_weighted(rgb1, rgb2): """ Determine the weighted distance between two rgb colors. :arg tuple rgb1: RGB color definition :arg tuple rgb2: RGB color definition :returns: Square of the distance between provided colors :rtype: float Similar to a standard distance formula, the values are weighted to approximate human perception of color differences For efficiency, the square of the distance is returned which is sufficient for comparisons """ red_mean = (rgb1[0] + rgb2[0]) / 2.0 return ((2 + red_mean / 256) * pow(rgb1[0] - rgb2[0], 2) + 4 * pow(rgb1[1] - rgb2[1], 2) + (2 + (255 - red_mean) / 256) * pow(rgb1[2] - rgb2[2], 2))
[docs] def dist_cie76(rgb1, rgb2): """ Determine distance between two rgb colors using the CIE94 algorithm. :arg tuple rgb1: RGB color definition :arg tuple rgb2: RGB color definition :returns: Square of the distance between provided colors :rtype: float For efficiency, the square of the distance is returned which is sufficient for comparisons """ l_1, a_1, b_1 = rgb_to_lab(*rgb1) l_2, a_2, b_2 = rgb_to_lab(*rgb2) return pow(l_1 - l_2, 2) + pow(a_1 - a_2, 2) + pow(b_1 - b_2, 2)
[docs] def dist_cie94(rgb1, rgb2): # pylint: disable=too-many-locals """ Determine distance between two rgb colors using the CIE94 algorithm. :arg tuple rgb1: RGB color definition :arg tuple rgb2: RGB color definition :returns: Square of the distance between provided colors :rtype: float For efficiency, the square of the distance is returned which is sufficient for comparisons """ l_1, a_1, b_1 = rgb_to_lab(*rgb1) l_2, a_2, b_2 = rgb_to_lab(*rgb2) s_l = k_l = k_c = k_h = 1 k_1 = 0.045 k_2 = 0.015 delta_l = l_1 - l_2 delta_a = a_1 - a_2 delta_b = b_1 - b_2 c_1 = sqrt(a_1 ** 2 + b_1 ** 2) c_2 = sqrt(a_2 ** 2 + b_2 ** 2) delta_c = c_1 - c_2 delta_h = sqrt(delta_a ** 2 + delta_b ** 2 + delta_c ** 2) s_c = 1 + k_1 * c_1 s_h = 1 + k_2 * c_1 return ((delta_l / (k_l * s_l)) ** 2 + # pylint: disable=superfluous-parens (delta_c / (k_c * s_c)) ** 2 + (delta_h / (k_h * s_h)) ** 2)
[docs] def dist_cie2000(rgb1, rgb2): # pylint: disable=too-many-locals """ Determine distance between two rgb colors using the CIE2000 algorithm. :arg tuple rgb1: RGB color definition :arg tuple rgb2: RGB color definition :returns: Square of the distance between provided colors :rtype: float For efficiency, the square of the distance is returned which is sufficient for comparisons """ s_l = k_l = k_c = k_h = 1 l_1, a_1, b_1 = rgb_to_lab(*rgb1) l_2, a_2, b_2 = rgb_to_lab(*rgb2) delta_l = l_2 - l_1 l_mean = (l_1 + l_2) / 2 c_1 = sqrt(a_1 ** 2 + b_1 ** 2) c_2 = sqrt(a_2 ** 2 + b_2 ** 2) c_mean = (c_1 + c_2) / 2 delta_c = c_1 - c_2 g_x = sqrt(c_mean ** 7 / (c_mean ** 7 + 25 ** 7)) h_1 = atan2(b_1, a_1 + (a_1 / 2) * (1 - g_x)) % 360 h_2 = atan2(b_2, a_2 + (a_2 / 2) * (1 - g_x)) % 360 if 0 in (c_1, c_2): delta_h_prime = 0 h_mean = h_1 + h_2 else: delta_h_prime = h_2 - h_1 if abs(delta_h_prime) <= 180: h_mean = (h_1 + h_2) / 2 else: if h_2 <= h_1: delta_h_prime += 360 else: delta_h_prime -= 360 h_mean = (h_1 + h_2 + 360) / 2 if h_1 + h_2 < 360 else (h_1 + h_2 - 360) / 2 delta_h = 2 * sqrt(c_1 * c_2) * sin(delta_h_prime / 2) t_x = (1 - 0.17 * cos(h_mean - 30) + 0.24 * cos(2 * h_mean) + 0.32 * cos(3 * h_mean + 6) - 0.20 * cos(4 * h_mean - 63)) s_l = 1 + (0.015 * (l_mean - 50) ** 2) / sqrt(20 + (l_mean - 50) ** 2) s_c = 1 + 0.045 * c_mean s_h = 1 + 0.015 * c_mean * t_x r_t = -2 * g_x * sin(abs(60 * exp(-1 * abs((delta_h - 275) / 25) ** 2))) delta_l = delta_l / (k_l * s_l) delta_c = delta_c / (k_c * s_c) delta_h = delta_h / (k_h * s_h) return delta_l ** 2 + delta_c ** 2 + delta_h ** 2 + r_t * delta_c * delta_h
COLOR_DISTANCE_ALGORITHMS = {'rgb': dist_rgb, 'rgb-weighted': dist_rgb_weighted, 'cie76': dist_cie76, 'cie94': dist_cie94, 'cie2000': dist_cie2000}