-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathciede_2000.java
66 lines (65 loc) · 2.96 KB
/
ciede_2000.java
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import static java.lang.Math.PI;
import static java.lang.Math.sqrt;
import static java.lang.Math.hypot;
import static java.lang.Math.atan2;
import static java.lang.Math.abs;
import static java.lang.Math.sin;
import static java.lang.Math.exp;
static double ciede_2000(final double l_1, final double a_1, final double b_1, final double l_2, final double a_2, final double b_2) {
// Working with the CIEDE2000 color-difference formula.
// k_l, k_c, k_h are parametric factors to be adjusted according to
// different viewing parameters such as textures, backgrounds...
final double k_l = 1.0, k_c = 1.0, k_h = 1.0;
double n = (hypot(a_1, b_1) + hypot(a_2, b_2)) * 0.5;
n = n * n * n * n * n * n * n;
// A factor involving chroma raised to the power of 7 designed to make
// the influence of chroma on the total color difference more accurate.
n = 1.0 + 0.5 * (1.0 - sqrt(n / (n + 6103515625.0)));
// hypot calculates the Euclidean distance while avoiding overflow/underflow.
final double c_1 = hypot(a_1 * n, b_1), c_2 = hypot(a_2 * n, b_2);
// atan2 is preferred over atan because it accurately computes the angle of
// a point (x, y) in all quadrants, handling the signs of both coordinates.
double h_1 = atan2(b_1, a_1 * n), h_2 = atan2(b_2, a_2 * n);
h_1 += 2.0 * PI * Boolean.compare(h_1 < 0.0, false);
h_2 += 2.0 * PI * Boolean.compare(h_2 < 0.0, false);
n = abs(h_2 - h_1);
// Cross-implementation consistent rounding.
if (PI - 1E-14 < n && n < PI + 1E-14)
n = PI;
// When the hue angles lie in different quadrants, the straightforward
// average can produce a mean that incorrectly suggests a hue angle in
// the wrong quadrant, the next lines handle this issue.
double h_m = 0.5 * h_1 + 0.5 * h_2, h_d = (h_2 - h_1) * 0.5;
if (PI < n) {
if (0.0 < h_d)
h_d -= PI;
else
h_d += PI;
h_m += PI;
}
final double p = (36.0 * h_m - 55.0 * PI);
n = (c_1 + c_2) * 0.5;
n = n * n * n * n * n * n * n;
// The hue rotation correction term is designed to account for the
// non-linear behavior of hue differences in the blue region.
final double r_t = -2.0 * sqrt(n / (n + 6103515625.0))
* sin(PI / 3.0 * exp(p * p / (-25.0 * PI * PI)));
n = (l_1 + l_2) * 0.5;
n = (n - 50.0) * (n - 50.0);
// Lightness.
final double l = (l_2 - l_1) / (k_l * (1.0 + 0.015 * n / sqrt(20.0 + n)));
// These coefficients adjust the impact of different harmonic
// components on the hue difference calculation.
final double t = 1.0 + 0.24 * sin(2.0 * h_m + PI / 2)
+ 0.32 * sin(3.0 * h_m + 8.0 * PI / 15.0)
- 0.17 * sin(h_m + PI / 3.0)
- 0.20 * sin(4.0 * h_m + 3.0 * PI / 20.0);
n = c_1 + c_2;
// Hue.
final double h = 2.0 * sqrt(c_1 * c_2) * sin(h_d) / (k_h * (1.0 + 0.0075 * n * t));
// Chroma.
final double c = (c_2 - c_1) / (k_c * (1.0 + 0.0225 * n));
// Returning the square root ensures that the result represents
// the "true" geometric distance in the color space.
return sqrt(l * l + h * h + c * c + c * h * r_t);
}