color_diff 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d4856b49dfb2ca4f6fc555def253b3aa586cdc01
4
+ data.tar.gz: 1f38316a2e62d067e0edf4772f03d787bc057be2
5
+ SHA512:
6
+ metadata.gz: 6d9d78fb545168a91a75f303a8e5f3fa36bd32bac2057b27621ccd992954ebf97d3ddc8a800d4575e439bab248910859f0f662113f38155716254b09d881356d
7
+ data.tar.gz: faceec60200cf5b91ad725db8d9400d329c6ba6a55aeb426f0f4a3b6f3f022af6381bc0eb5426da03e0df9460be88ed6da5146b3fab49dae2447777924f63ec4
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Daniel Hanson
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
14
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
15
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
16
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
17
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
18
+ SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # Color Diff
2
+
3
+ Calculating differences between colors using the CIEDE2000 algorithm
4
+
5
+ ## Install
6
+
7
+ gem install color_diff
8
+
9
+ ## Usage
10
+
11
+ Difference between two colors:
12
+
13
+ yellow = ColorDiff::Color::RGB.new 255, 255, 0
14
+ gold = ColorDiff::Color::RGB.new 255, 215, 0
15
+
16
+ ColorDiff.between yellow, gold
17
+ # => 11.584521223499245
18
+
19
+ Picking the most similar color from a list:
20
+
21
+ red = ColorDiff::Color::RGB.new 255, 0, 0
22
+ blue = ColorDiff::Color::RGB.new 0, 0, 255
23
+ yellow = ColorDiff::Color::RGB.new 255, 255, 0
24
+ gold = ColorDiff::Color::RGB.new 255, 215, 0
25
+
26
+ list = ColorDiff::List.new [red, blue, yellow]
27
+ list.closest_to(gold) == yellow
28
+ # => true
29
+
30
+ Building a map of nearest match colors from a finite palette:
31
+
32
+ red = ColorDiff::Color::RGB.new 255, 0, 0
33
+ black = ColorDiff::Color::RGB.new 0, 0, 0
34
+ dark_gray = ColorDiff::Color::RGB.new 80, 80, 80
35
+ maroon = ColorDiff::Color::RGB.new 128, 0, 0
36
+
37
+ list = ColorDiff::List.new [red, black]
38
+ palette = [dark_gray, maroon]
39
+
40
+ list.closest_to_map(palette)
41
+ # => { 'R255G0B0' => maroon, 'R0B0G0' => dark_gray }
@@ -0,0 +1,35 @@
1
+ require 'color_diff/color/rgb'
2
+ require 'color_diff/color/xyz'
3
+ require 'color_diff/color/lab'
4
+ require 'color_diff/ciede2000'
5
+ require 'color_diff/list'
6
+
7
+ module ColorDiff
8
+ # = Color Diff
9
+ #
10
+ # Color Diff contains a number of methods to assist in measuring how similar
11
+ # or dissimilar colors are from one another
12
+ #
13
+ # == Simple Comparison
14
+ #
15
+ # Two color objects can be compared like so:
16
+ #
17
+ # yellow = ColorDiff::Color::RGB.new(255, 255, 0)
18
+ # gold = ColorDiff::Color::RGB.new(255, 215, 0)
19
+ # ColorDiff.between(yellow, gold) => 11.584521223499245
20
+ #
21
+ # The higher the result the more dissimilar the colors, e.g.
22
+ #
23
+ # blue = ColorDiff::Color::RGB.new(0, 0, 255)
24
+ # red = ColorDiff::Color::RGB.new(255, 0, 0)
25
+ # ColorDiff.between(blue, red) => 52.87867414046132
26
+ #
27
+ # See ColorDiff::List for example of more complex comparisons
28
+
29
+ def self.between(lab1, lab2)
30
+ lab1 = lab1.to_lab unless lab1.is_a? Color::Lab
31
+ lab2 = lab2.to_lab unless lab2.is_a? Color::Lab
32
+
33
+ ColorDiff::Ciede2000.diff(lab1, lab2)
34
+ end
35
+ end
@@ -0,0 +1,115 @@
1
+ module ColorDiff
2
+ class Ciede2000
3
+ def self.diff(c1, c2)
4
+ # get lab values for color 1
5
+ l1 = c1.l
6
+ a1 = c1.a
7
+ b1 = c1.b
8
+
9
+ # get lab values for color 2
10
+ l2 = c2.l
11
+ a2 = c2.a
12
+ b2 = c2.b
13
+
14
+ # weight factors
15
+ kl = 1
16
+ kc = 1
17
+ kh = 1
18
+
19
+ # Step 1: Calculate c1p, c2p, h1p, h2p
20
+ #
21
+ c1 = Math.sqrt((a1 ** 2) + (b1 ** 2))
22
+ c2 = Math.sqrt((a2 ** 2) + (b2 ** 2))
23
+
24
+ a_c1_c2 = (c1 + c2) / 2.0
25
+
26
+ g = 0.5 * (1 - Math.sqrt((a_c1_c2 ** 7.0) / ((a_c1_c2 ** 7.0) + (25.0 ** 7.0))))
27
+
28
+ a1p = (1.0 + g) * a1
29
+ a2p = (1.0 + g) * a2
30
+ c1p = Math.sqrt((a1p ** 2) + (b1 ** 2))
31
+ c2p = Math.sqrt((a2p ** 2) + (b2 ** 2))
32
+ h1p = hp_f(b1, a1p)
33
+ h2p = hp_f(b2, a2p)
34
+
35
+ # Step 2: calculate dlp, dcp, dhp
36
+ #
37
+ dlp = l2 - l1
38
+ dcp = c2p - c1p
39
+ dhp_tmp = dhp_f(c1, c2, h1p, h2p)
40
+ dhp = 2 * Math.sqrt(c1p * c2p) * Math.sin(radians(dhp_tmp) / 2.0)
41
+
42
+ # Step 3: calculate CIEDE2000 Color-Difference
43
+ #
44
+ a_l = (l1 + l2) / 2.0
45
+ a_cp = (c1p + c2p) / 2.0
46
+ a_hp = a_hp_f(c1, c2, h1p, h2p)
47
+ t = 1 -
48
+ 0.17 * Math.cos(radians(a_hp - 30)) +
49
+ 0.24 * Math.cos(radians(2 * a_hp)) +
50
+ 0.32 * Math.cos(radians(3 * a_hp + 6)) -
51
+ 0.20 * Math.cos(radians(4 * a_hp - 63))
52
+ d_ro = 30 * Math.exp(-(((a_hp - 275) / 25) ** 2))
53
+ rc = Math.sqrt((a_cp ** 7.0) / ((a_cp ** 7.0) + (25.0 ** 7.0)))
54
+ sl = 1 + ((0.015 * ((a_l - 50) ** 2)) / Math.sqrt(20 + ((a_l - 50) ** 2.0)))
55
+ sc = 1 + 0.045 * a_cp
56
+ sh = 1 + 0.015 * a_cp * t
57
+ rt = -2 * rc * Math.sin(radians(2 * d_ro))
58
+ de = Math.sqrt(
59
+ ((dlp / (sl * kl)) ** 2) +
60
+ ((dcp / (sc * kc)) ** 2) +
61
+ ((dhp / (sh * kh)) ** 2) +
62
+ rt * (dcp / (sc * kc)) * (dhp / (sh * kh)) )
63
+
64
+ de
65
+ end
66
+
67
+ def self.degrees(n)
68
+ 180 / Math::PI * n
69
+ end
70
+
71
+ def self.radians(n)
72
+ Math::PI / 180 * n
73
+ end
74
+
75
+ def self.hp_f(x, y)
76
+ 0 if x.zero? && y.zero?
77
+
78
+ tmphp = degrees Math.atan2(x, y)
79
+ if tmphp >= 0
80
+ tmphp
81
+ else
82
+ tmphp + 360
83
+ end
84
+ end
85
+
86
+ def self.dhp_f(c1, c2, h1p, h2p)
87
+ diff = h2p - h1p
88
+
89
+ if c1 * c2 == 0
90
+ 0
91
+ elsif diff.abs <= 180
92
+ diff
93
+ elsif diff > 180
94
+ diff - 360
95
+ elsif diff < -180
96
+ diff + 360
97
+ end
98
+ end
99
+
100
+ def self.a_hp_f(c1, c2, h1p, h2p)
101
+ sum = h1p + h2p
102
+ diff = h1p - h2p
103
+
104
+ if c1 * c2 == 0
105
+ sum
106
+ elsif diff.abs <= 180
107
+ sum / 2.0
108
+ elsif diff.abs > 180 && sum < 360
109
+ (sum + 360) / 2.0
110
+ elsif diff.abs > 180 && sum >= 360
111
+ (sum - 360) / 2.0
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,46 @@
1
+ module ColorDiff
2
+ module Color
3
+ class Lab
4
+ attr_reader :l, :a, :b
5
+
6
+ PRECISION = 3
7
+
8
+ def initialize(l = 0, a = 0, b = 0)
9
+ @l = l
10
+ @a = a
11
+ @b = b
12
+ end
13
+
14
+ def self.from_xyz(xyz)
15
+ ref_x = 95.047
16
+ ref_y = 100.000
17
+ ref_z = 108.883
18
+ x = xyz.x / ref_x
19
+ y = xyz.y / ref_y
20
+ z = xyz.z / ref_z
21
+
22
+ x = normalize_xyz_component(x)
23
+ y = normalize_xyz_component(y)
24
+ z = normalize_xyz_component(z)
25
+
26
+ l = (116 * y) - 16
27
+ a = 500 * (x - y)
28
+ b = 200 * (y - z)
29
+
30
+ new(l, a, b)
31
+ end
32
+
33
+ def self.normalize_xyz_component(c)
34
+ if c > 0.008856
35
+ c ** (1 / 3.0)
36
+ else
37
+ (c * 7.787) + (16 / 116.0)
38
+ end
39
+ end
40
+
41
+ def to_s
42
+ "L#{@l.round(PRECISION)}A#{@a.round(PRECISION)}B#{@b.round(PRECISION)}"
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,25 @@
1
+ module ColorDiff
2
+ module Color
3
+ class RGB
4
+ attr_reader :r, :g, :b
5
+
6
+ def initialize(r = 0, g = 0, b = 0)
7
+ @r = r
8
+ @g = g
9
+ @b = b
10
+ end
11
+
12
+ def to_xyz
13
+ Xyz.from_rgb(self)
14
+ end
15
+
16
+ def to_lab
17
+ to_xyz.to_lab
18
+ end
19
+
20
+ def to_s
21
+ "R#{@r}G#{@g}B#{@b}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ module ColorDiff
2
+ module Color
3
+ class Xyz
4
+ attr_reader :x, :y, :z
5
+
6
+ def initialize(x = 0, y = 0, z = 0)
7
+ @x = x
8
+ @y = y
9
+ @z = z
10
+ end
11
+
12
+ def to_lab
13
+ Lab.from_xyz(self)
14
+ end
15
+
16
+ def self.from_rgb(rgb)
17
+ r = normalize_rgb_component rgb.r
18
+ g = normalize_rgb_component rgb.g
19
+ b = normalize_rgb_component rgb.b
20
+
21
+ x = r * 0.4124 + g * 0.3576 + b * 0.1805
22
+ y = r * 0.2126 + g * 0.7152 + b * 0.0722
23
+ z = r * 0.0193 + g * 0.1192 + b * 0.9505
24
+
25
+ new(x, y, z)
26
+ end
27
+
28
+ def self.normalize_rgb_component(c)
29
+ c = c / 255.0
30
+
31
+ if c > 0.04045
32
+ c = ((c + 0.055) / 1.055) ** 2.4
33
+ else
34
+ c = c / 12.92
35
+ end
36
+
37
+ c * 100
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,67 @@
1
+ module ColorDiff
2
+ class List
3
+ # +items+ An array of ColorDiff::Colors
4
+ def initialize(items)
5
+ @items = items
6
+ end
7
+
8
+ # Compares all +items+ in the list to the supplied +color+ and returns
9
+ # the most similar
10
+ #
11
+ # +color+ ColorDiff::Color to compare against the list
12
+ #
13
+ # == Example
14
+ #
15
+ # red = ColorDiff::Color::RGB.new(255, 0, 0)
16
+ # blue = ColorDiff::Color::RGB.new(0, 0, 255)
17
+ # yellow = ColorDiff::Color::RGB.new(255, 255, 0)
18
+ # gold = ColorDiff::Color::RGB.new(255, 215, 0)
19
+ #
20
+ # list = ColorDiff::List.new [red, blue, yellow]
21
+ # list.closest_to(gold) == yellow => true
22
+ #
23
+ def closest_to(color)
24
+ closest_color = nil
25
+ closest_diff = 100
26
+
27
+ @items.each do |item|
28
+ diff = ColorDiff.between(color, item)
29
+ if diff < closest_diff
30
+ closest_diff = diff
31
+ closest_color = item
32
+ end
33
+ end
34
+
35
+ closest_color
36
+ end
37
+
38
+ # Compares all +items+ in the list against the supplied +palette+ and
39
+ # maps each item to the closest palette color. The resulting hash is
40
+ # returned
41
+ #
42
+ # +palette+ Array of ColorDiff::Colors to compare against the list
43
+ #
44
+ # == Example
45
+ #
46
+ # red = ColorDiff::Color::RGB.new(255, 0, 0)
47
+ # black = ColorDiff::Color::RGB.new(0, 0, 0)
48
+ # dark_gray = ColorDiff::Color::RGB.new(80, 80, 80)
49
+ # maroon = ColorDiff::Color::RGB.new(128, 0, 0)
50
+ #
51
+ # list = ColorDiff::List.new [red, black]
52
+ # palette = [dark_gray, maroon]
53
+ #
54
+ # list.closest_to_map(palette) =>
55
+ # { 'R255G0B0' => maroon, 'R0B0G0' => dark_gray }
56
+ #
57
+ def closest_to_map(palette)
58
+ map = {}
59
+ options = List.new(palette)
60
+ @items.each do |item|
61
+ map[item.to_s] = options.closest_to item
62
+ end
63
+
64
+ map
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ module ColorDiff
2
+ VERSION = '0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: color_diff
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Hanson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-06-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ description: Calculate RGB color distances using CIEDE2000 formula
28
+ email: hansondr@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - LICENSE
34
+ - README.md
35
+ - lib/color_diff.rb
36
+ - lib/color_diff/ciede2000.rb
37
+ - lib/color_diff/color/lab.rb
38
+ - lib/color_diff/color/rgb.rb
39
+ - lib/color_diff/color/xyz.rb
40
+ - lib/color_diff/list.rb
41
+ - lib/color_diff/version.rb
42
+ homepage: https://github.com/hansondr/color_diff
43
+ licenses:
44
+ - MIT
45
+ metadata: {}
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 2.6.8
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Calculate rgb color distances using CIEDE2000 formula
66
+ test_files: []