Module: Colorscore::Metrics

Defined in:
lib/colorscore/metrics.rb

Class Method Summary collapse

Class Method Details

.degrees(radians) ⇒ Object


139
# File 'lib/colorscore/metrics.rb', line 139

def self.degrees(radians); radians * 180 / Math::PI; end

.delta_e_cie_2000(l1, a1, b1, l2, a2, b2) ⇒ Object

Ported from colormath for Python.


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
67
68
69
70
71
72
73
74
# File 'lib/colorscore/metrics.rb', line 16

def self.delta_e_cie_2000(l1, a1, b1, l2, a2, b2)
  kl = kc = kh = 1
  
  avg_lp = (l1 + l2) / 2.0
  c1 = Math.sqrt((a1 ** 2) + (b1 ** 2))
  c2 = Math.sqrt((a2 ** 2) + (b2 ** 2))
  avg_c1_c2 = (c1 + c2) / 2.0

  g = 0.5 * (1 - Math.sqrt((avg_c1_c2 ** 7.0) / ((avg_c1_c2 ** 7.0) + (25.0 ** 7.0))))

  a1p = (1.0 + g) * a1
  a2p = (1.0 + g) * a2
  c1p = Math.sqrt((a1p ** 2) + (b1 ** 2))
  c2p = Math.sqrt((a2p ** 2) + (b2 ** 2))
  avg_c1p_c2p = (c1p + c2p) / 2.0

  if degrees(Math.atan2(b1,a1p)) >= 0
    h1p = degrees(Math.atan2(b1,a1p))
  else
    h1p = degrees(Math.atan2(b1,a1p)) + 360
  end

  if degrees(Math.atan2(b2,a2p)) >= 0
    h2p = degrees(Math.atan2(b2,a2p))
  else
    h2p = degrees(Math.atan2(b2,a2p)) + 360
  end

  if (h1p - h2p).abs > 180
    avg_hp = (h1p + h2p + 360) / 2.0
  else
    avg_hp = (h1p + h2p) / 2.0
  end

  t = 1 - 0.17 * Math.cos(radians(avg_hp - 30)) + 0.24 * Math.cos(radians(2 * avg_hp)) + 0.32 * Math.cos(radians(3 * avg_hp + 6)) - 0.2 * Math.cos(radians(4 * avg_hp - 63))

  diff_h2p_h1p = h2p - h1p
  if diff_h2p_h1p.abs <= 180
    delta_hp = diff_h2p_h1p
  elsif diff_h2p_h1p.abs > 180 && h2p <= h1p
    delta_hp = diff_h2p_h1p + 360
  else
    delta_hp = diff_h2p_h1p - 360
  end

  delta_lp = l2 - l1
  delta_cp = c2p - c1p
  delta_hp = 2 * Math.sqrt(c2p * c1p) * Math.sin(radians(delta_hp) / 2.0)

  s_l = 1 + ((0.015 * ((avg_lp - 50) ** 2)) / Math.sqrt(20 + ((avg_lp - 50) ** 2.0)))
  s_c = 1 + 0.045 * avg_c1p_c2p
  s_h = 1 + 0.015 * avg_c1p_c2p * t

  delta_ro = 30 * Math.exp(-((((avg_hp - 275) / 25) ** 2.0)))
  r_c = Math.sqrt(((avg_c1p_c2p ** 7.0)) / ((avg_c1p_c2p ** 7.0) + (25.0 ** 7.0)));
  r_t = -2 * r_c * Math.sin(2 * radians(delta_ro))

  delta_e = Math.sqrt(((delta_lp / (s_l * kl)) ** 2) + ((delta_cp / (s_c * kc)) ** 2) + ((delta_hp / (s_h * kh)) ** 2) + r_t * (delta_cp / (s_c * kc)) * (delta_hp / (s_h * kh)))
end

.distance(color_1, color_2) ⇒ Object


7
8
9
10
11
12
13
# File 'lib/colorscore/metrics.rb', line 7

def self.distance(color_1, color_2)
  l1, a1, b1 = xyz_to_lab(*rgb_to_xyz(color_1))
  l2, a2, b2 = xyz_to_lab(*rgb_to_xyz(color_2))
  
  distance = delta_e_cie_2000(l1, a1, b1, l2, a2, b2)
  scale(distance, 0..100)
end

.radians(degrees) ⇒ Object


138
# File 'lib/colorscore/metrics.rb', line 138

def self.radians(degrees); degrees * Math::PI / 180; end

.rgb_to_xyz(color) ⇒ Object


76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/colorscore/metrics.rb', line 76

def self.rgb_to_xyz(color)
  color = color.to_rgb
  r, g, b = color.r, color.g, color.b
  
  # assuming sRGB (D65)
  r = (r <= 0.04045) ? r/12.92 : ((r+0.055)/1.055) ** 2.4
  g = (g <= 0.04045) ? g/12.92 : ((g+0.055)/1.055) ** 2.4
  b = (b <= 0.04045) ? b/12.92 : ((b+0.055)/1.055) ** 2.4
  
  r *= 100
  g *= 100
  b *= 100
  
  x = 0.412453*r + 0.357580*g + 0.180423*b
  y = 0.212671*r + 0.715160*g + 0.072169*b
  z = 0.019334*r + 0.119193*g + 0.950227*b
  
  [x, y, z]
end

.scale(number, from_range, to_range = 0..1, clamp = true) ⇒ Object


126
127
128
129
130
131
132
133
134
135
136
# File 'lib/colorscore/metrics.rb', line 126

def self.scale(number, from_range, to_range=0..1, clamp=true)
  if clamp && number <= from_range.begin
    position = 0
  elsif clamp && number >= from_range.end
    position = 1
  else
    position = (number - from_range.begin).to_f / (from_range.end - from_range.begin)
  end
  
  position * (to_range.end - to_range.begin) + to_range.begin
end

.similarity(a, b) ⇒ Object


3
4
5
# File 'lib/colorscore/metrics.rb', line 3

def self.similarity(a, b)
  1 - distance(a, b)
end

.xyz_to_lab(x, y, z) ⇒ Object


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/colorscore/metrics.rb', line 96

def self.xyz_to_lab(x, y, z)
  x /= 95.047
  y /= 100.000
  z /= 108.883

  if x > 0.008856
    x = x ** (1.0/3)
  else
    x = (7.787 * x) + (16.0 / 116)
  end
  
  if y > 0.008856
    y = y ** (1.0/3)
  else
    y = (7.787 * y) + (16.0 / 116)
  end
  
  if z > 0.008856
    z = z ** (1.0/3)
  else
    z = (7.787 * z) + (16.0 / 116)
  end

  l = (116.0 * y) - 16.0
  a = 500.0 * (x - y)
  b = 200.0 * (y - z)

  [l, a, b]
end