Coverage for /usr/lib/python3/dist-packages/colorzero/deltae.py: 14%
53 statements
« prev ^ index » next coverage.py v7.2.7, created at 2024-02-10 12:38 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2024-02-10 12:38 +0000
1# vim: set et sw=4 sts=4 fileencoding=utf-8:
2#
3# The colorzero color library
4# Copyright (c) 2016-2018 Dave Jones <dave@waveform.org.uk>
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are met:
8#
9# * Redistributions of source code must retain the above copyright
10# notice, this list of conditions and the following disclaimer.
11# * Redistributions in binary form must reproduce the above copyright
12# notice, this list of conditions and the following disclaimer in the
13# documentation and/or other materials provided with the distribution.
14# * Neither the name of the copyright holder nor the
15# names of its contributors may be used to endorse or promote products
16# derived from this software without specific prior written permission.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28# POSSIBILITY OF SUCH DAMAGE.
30"Defines the various algorithms for :meth:`Color.difference`."
32from __future__ import (
33 unicode_literals,
34 print_function,
35 division,
36 absolute_import,
37)
39from math import sqrt, atan2, degrees, radians, sin, cos, exp
41# Lots of the delta-e functions use single character parameter names and
42# variables internally; this is is normal and in keeping with most of the
43# referenced sources
44# pylint: disable=invalid-name
47def euclid(color1, color2):
48 """
49 Calculates color difference as a simple `Euclidean distance`_ by treating
50 the three components as spatial dimensions.
52 .. note::
54 This function will return considerably different values to the other
55 difference functions. In particular, the maximum "difference" will
56 be √3 which is much smaller than the output of the CIE functions.
58 .. _Euclidean distance: https://en.wikipedia.org/wiki/Euclidean_distance
59 """
60 return sqrt(sum((e1 - e2) ** 2 for e1, e2 in zip(color1, color2)))
63def cie1976(color1, color2):
64 """
65 Calculates color difference according to the `CIE 1976`_ formula.
66 Effectively this is the Euclidean formula, but with CIE L*a*b* components
67 instead of RGB.
69 .. _CIE 1976: https://en.wikipedia.org/wiki/Color_difference#CIE76
70 """
71 return sqrt(sum((e1 - e2) ** 2 for e1, e2 in zip(color1, color2)))
74def cie1994(color1, color2, method):
75 """
76 Calculates color difference according to the `CIE 1994`_ formula. The
77 *method* can be either "cie1994g" for the "graphical" biases, or "cie1994t"
78 for the "textile" biases. The CIE1994 is also basically the Euclidean
79 formula (with biases) but in CIE L*C*H* space.
81 .. _CIE 1994: https://en.wikipedia.org/wiki/Color_difference#CIE94
82 """
83 C1 = sqrt(color1.a ** 2 + color1.b ** 2)
84 C2 = sqrt(color2.a ** 2 + color2.b ** 2)
86 dL = color1.l - color2.l
87 dC = C1 - C2
88 # Don't bother with the sqrt here as due to limited float precision
89 # we can wind up with a domain error (because the value is ever so
90 # slightly negative - try it with black'n'white for an example), and we're
91 # just going to square the result in the final equation anyway
92 dH2 = (color1.a - color2.a) ** 2 + (color1.b - color2.b) ** 2 - dC ** 2
94 kL, K1, K2 = {
95 'cie1994g': (1, 0.045, 0.015),
96 'cie1994t': (2, 0.048, 0.014),
97 }[method]
98 SC = 1 + K1 * C1
99 SH = 1 + K2 * C1
100 return sqrt(
101 (dL / kL) ** 2 +
102 (dC / SC) ** 2 +
103 (dH2 / SH ** 2)
104 )
107def cie1994g(color1, color2):
108 """
109 Calculates color difference according to the `CIE 1994`_ formula with the
110 "textile" bias. See :func:`cie1994` for further information.
112 .. _CIE 1994: https://en.wikipedia.org/wiki/Color_difference#CIE94
113 """
114 return cie1994(color1, color2, 'cie1994g')
117def cie1994t(color1, color2):
118 """
119 Calculates color difference according to the `CIE 1994`_ formula with the
120 "graphics" bias. See :func:`cie1994` for further information.
122 .. _CIE 1994: https://en.wikipedia.org/wiki/Color_difference#CIE94
123 """
124 return cie1994(color1, color2, 'cie1994t')
127def ciede2000(color1, color2):
128 """
129 Calculates color difference according to the `CIEDE 2000`_ formula. This is
130 the most accurate algorithm currently implemented but also the most complex
131 and slowest. Like CIE1994 it is largely based in CIE L*C*h* space, but with
132 several modifications to account for perceptual uniformity flaws.
134 .. _CIEDE 2000: https://en.wikipedia.org/wiki/Color_difference#CIEDE2000
135 """
136 # See WP article and Sharma 2005 for important implementation notes:
137 # http://www.ece.rochester.edu/~gsharma/ciede2000/ciede2000noteCRNA.pdf
138 #
139 # Yes, there's lots of locals; but this is easiest to understand as it's a
140 # near straight translation of the math
141 # pylint: disable=too-many-locals
143 C_ = (
144 sqrt(color1.a ** 2 + color1.b ** 2) +
145 sqrt(color2.a ** 2 + color2.b ** 2)
146 ) / 2
148 G = (1 - sqrt(C_ ** 7 / (C_ ** 7 + 25 ** 7))) / 2
149 a1_prime = (1 + G) * color1.a
150 a2_prime = (1 + G) * color2.a
152 C1_prime = sqrt(a1_prime ** 2 + color1.b ** 2)
153 C2_prime = sqrt(a2_prime ** 2 + color2.b ** 2)
154 L_ = (color1.l + color2.l) / 2
155 C_ = (C1_prime + C2_prime) / 2
157 h1 = (
158 0.0 if color1.b == a1_prime == 0 else
159 degrees(atan2(color1.b, a1_prime)) % 360
160 )
161 h2 = (
162 0.0 if color2.b == a2_prime == 0 else
163 degrees(atan2(color2.b, a2_prime)) % 360
164 )
165 if C1_prime * C2_prime == 0.0:
166 dh = 0.0
167 h_ = h1 + h2
168 elif abs(h1 - h2) <= 180:
169 dh = h2 - h1
170 h_ = (h1 + h2) / 2
171 else:
172 if h2 > h1:
173 dh = h2 - h1 - 360
174 else:
175 dh = h2 - h1 + 360
176 if h1 + h2 >= 360:
177 h_ = (h1 + h2 - 360) / 2
178 else:
179 h_ = (h1 + h2 + 360) / 2
181 dL = color2.l - color1.l
182 dC = C2_prime - C1_prime
183 dH = 2 * sqrt(C1_prime * C2_prime) * sin(radians(dh / 2))
185 T = (
186 1 -
187 0.17 * cos(radians(h_ - 30)) +
188 0.24 * cos(radians(2 * h_)) +
189 0.32 * cos(radians(3 * h_ + 6)) -
190 0.20 * cos(radians(4 * h_ - 63))
191 )
192 SL = 1 + (0.015 * (L_ - 50) ** 2) / sqrt(20 + (L_ - 50) ** 2)
193 SC = 1 + 0.045 * C_
194 SH = 1 + 0.015 * C_ * T
195 RT = (
196 -2 * sqrt(C_ ** 7 / (C_ ** 7 + 25 ** 7)) *
197 sin(radians(60 * exp(-(((h_ - 275) / 25) ** 2))))
198 )
200 return sqrt(
201 (dL / SL) ** 2 +
202 (dC / SC) ** 2 +
203 (dH / SH) ** 2 +
204 RT * (dC / SC) * (dH / SH)
205 )