color_extract 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +5 -0
- data/Rakefile +1 -0
- data/color_extract.gemspec +25 -0
- data/example/extract.rb +18 -0
- data/lib/color_extract/analytics.rb +178 -0
- data/lib/color_extract/color_util.rb +131 -0
- data/lib/color_extract/palette.rb +188 -0
- data/lib/color_extract/version.rb +3 -0
- data/lib/color_extract.rb +4 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: fcdf8c9b4f5cae0c0df68f6253ac81af6435457a
|
4
|
+
data.tar.gz: 9ff5cc7c5c43e25a1183a440e18e7bf8cd2c27d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 79583d8b9e76d7f89ff0abd2f8a4b7646d72d807337516e7b0f2480ec8cee6b926d07e6eb30ae858cd0b2513c407678e31aa44677709549604acd77cbe581434
|
7
|
+
data.tar.gz: 4199a9b1d150627ac1b7a9b68cee415f46364cfd07e231faf8423c21602e97e0abd38ebe9ddb36f7652af923cdfab2ed5adc6fd9a7dadc6a7f93dec789b4648a
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 qhwa
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'color_extract/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "color_extract"
|
8
|
+
spec.version = ColorExtract::VERSION
|
9
|
+
spec.authors = ["qhwa"]
|
10
|
+
spec.email = ["qhwa@163.com"]
|
11
|
+
spec.summary = %q{extract color information from images}
|
12
|
+
spec.description = %q{extract color information from images}
|
13
|
+
spec.homepage = "https://github.com/qhwa/color_extract"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "colorscore", "~> 0.0.4"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
data/example/extract.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift File.expand_path( '../../lib', __FILE__ )
|
2
|
+
require 'color_extract'
|
3
|
+
|
4
|
+
file = ARGV[0]
|
5
|
+
if file
|
6
|
+
puts "File: #{file}"
|
7
|
+
ColorExtract::Analytics.new( file ).valuable_colors.each do |per, color|
|
8
|
+
print " - %#.2f%% %s %s" % [per*100, color.html, color.css_hsl]
|
9
|
+
print "\n"
|
10
|
+
end
|
11
|
+
|
12
|
+
puts "palette:"
|
13
|
+
ColorExtract::Palette.new( file ).palette.each do |name, color|
|
14
|
+
next unless color
|
15
|
+
print " - %s : %s %s" % [ name.to_s.ljust(6), color.html, color.css_hsl ]
|
16
|
+
print "\n"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
require 'colorscore'
|
2
|
+
|
3
|
+
module ColorExtract
|
4
|
+
|
5
|
+
MAX_VISIBLE_COLORS = 128
|
6
|
+
|
7
|
+
# Public: 分析图片的色彩信息,相似的颜色会合并在一起
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
#
|
11
|
+
# img = "http://distilleryimage2...jpg"
|
12
|
+
# colors = ColorExtract::Analytics.new( img ).valuable_colors
|
13
|
+
# colors.each do |percent, color|
|
14
|
+
# puts "#{color.html} : #{percent}"
|
15
|
+
# end
|
16
|
+
class Analytics
|
17
|
+
|
18
|
+
include ColorUtil
|
19
|
+
|
20
|
+
attr_reader :merge_factor
|
21
|
+
|
22
|
+
def initialize( img )
|
23
|
+
@img = img
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: 获取颜色数组
|
27
|
+
#
|
28
|
+
# - merge_factor: 颜色合并系数,数字越大,相似的颜色越容易被合并在一起
|
29
|
+
#
|
30
|
+
# Returns 颜色数组,数组的每个元素都是 [percent, color] 结构
|
31
|
+
def valuable_colors( merge_factor: 5 )
|
32
|
+
@merge_factor = merge_factor
|
33
|
+
@colors ||= calc_valuable_colors
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def calc_valuable_colors
|
39
|
+
@colors = @raw_colors = visible_colors
|
40
|
+
|
41
|
+
# 黑白灰是不具有色相的颜色,可以和任何色相的颜色搭配
|
42
|
+
# 所以不是我们关心的颜色
|
43
|
+
remove_gray_colors
|
44
|
+
|
45
|
+
# 去掉灰色之后重新计算一下百分比,这样后期就不会有
|
46
|
+
# 太多比例特别少的颜色,防止色彩丢失过多
|
47
|
+
update_percents
|
48
|
+
|
49
|
+
# 视觉上相近的颜色都合并成一种进行统计
|
50
|
+
# 这样得到的颜色比例会更加精确
|
51
|
+
merge_similar_colors
|
52
|
+
|
53
|
+
# 数量太少的颜色很有可能是主色的过渡
|
54
|
+
# 可以去掉和主色同色系的低比例色彩
|
55
|
+
remove_few_colors
|
56
|
+
|
57
|
+
return @colors
|
58
|
+
end
|
59
|
+
|
60
|
+
public
|
61
|
+
|
62
|
+
# Public: 获取可见的颜色,这里的颜色是没有经过相似合并的
|
63
|
+
#
|
64
|
+
# Returns 可见颜色数组,数组每个元素都是 [percent, color] 结构
|
65
|
+
def visible_colors
|
66
|
+
Colorscore::Histogram.new( @img, MAX_VISIBLE_COLORS).scores
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def remove_gray_colors
|
72
|
+
@colors.reject! do |per, color|
|
73
|
+
hsl = color.to_hsl
|
74
|
+
# 灰色系颜色
|
75
|
+
l, a, b = *rgb2lab(color.to_rgb)
|
76
|
+
hsl.s < 0.1 or
|
77
|
+
# 接近白色
|
78
|
+
l > 83 or
|
79
|
+
# 接近黑色
|
80
|
+
l < 25
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def update_percents
|
85
|
+
total = @raw_colors.reduce(0) {|sum, info| sum + info[0]}
|
86
|
+
@colors.each do |info|
|
87
|
+
per, c = *info
|
88
|
+
info[0] = per / total
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# TODO: 重构这个函数
|
93
|
+
# 这里应该可以逻辑更加简单一些的
|
94
|
+
def merge_similar_colors
|
95
|
+
|
96
|
+
@colors.each do |info|
|
97
|
+
per, c = *info
|
98
|
+
info[1] = pure( dither( c ), s: nil, l:nil )
|
99
|
+
end
|
100
|
+
|
101
|
+
auto_link_colors!
|
102
|
+
|
103
|
+
new_colors = {}
|
104
|
+
@colors.each do |per, color|
|
105
|
+
link_to = parent_color(color)
|
106
|
+
if link_to
|
107
|
+
new_colors[link_to] ||= 0
|
108
|
+
new_colors[link_to] += per
|
109
|
+
else
|
110
|
+
new_colors[color.html] = per
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
@colors = new_colors.map do |color_html, per|
|
115
|
+
[per, Color::RGB.from_html(color_html)]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def auto_link_colors!
|
120
|
+
@merge_link = {}
|
121
|
+
colors = @colors.sort_by {|per, c| c.to_hsl.h }
|
122
|
+
len = colors.size - 1
|
123
|
+
|
124
|
+
0.upto(len) do |i|
|
125
|
+
per, color, lab = *colors[i]
|
126
|
+
hsl = color.to_hsl
|
127
|
+
|
128
|
+
(i+1).upto(len) do |j|
|
129
|
+
per2, color2, lab2 = *colors[j]
|
130
|
+
|
131
|
+
if should_merge?(color, color2)
|
132
|
+
# 相近的颜色,合并到面积更大的那种颜色
|
133
|
+
# 这里尝试过另外的策略,比如合并到更鲜艳
|
134
|
+
# 的那种颜色,但是效果不是很好
|
135
|
+
if per >= per2
|
136
|
+
@merge_link[color.html] = parent_color(color2)
|
137
|
+
else
|
138
|
+
@merge_link[color2.html] = parent_color(color)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def should_merge?( color1, color2 )
|
146
|
+
hue_similarity( color1, color2 ) <= 20 && similarity( color1, color2, ignore_lightness: true ) < @merge_factor
|
147
|
+
end
|
148
|
+
|
149
|
+
def parent_color color
|
150
|
+
parent = @merge_link[color.html]
|
151
|
+
if parent && parent != color.html
|
152
|
+
parent_color( Color::RGB.from_html(parent) )
|
153
|
+
else
|
154
|
+
color.html
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def remove_few_colors
|
159
|
+
sorted_colors = @colors.sort_by {|per,c| -per}
|
160
|
+
if sorted_colors.size > 5
|
161
|
+
sorted_colors.reject! {|per, c| per < 0.0005 }
|
162
|
+
end
|
163
|
+
common_colors = sorted_colors
|
164
|
+
little_colors = sorted_colors.select {|per, c| per < 0.05 }
|
165
|
+
|
166
|
+
little_colors.select! do |per, c|
|
167
|
+
common_colors.any? do |common_per, common_color|
|
168
|
+
common_per > per && hue_similarity( c, common_color ) <= 10
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
@colors = sorted_colors - little_colors
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module ColorExtract
|
2
|
+
|
3
|
+
module ColorUtil
|
4
|
+
|
5
|
+
# Corresponds roughly to RGB brighter/darker
|
6
|
+
K = 18
|
7
|
+
|
8
|
+
# D65 standard referent
|
9
|
+
X = 0.950470
|
10
|
+
Y = 1
|
11
|
+
Z = 1.088830
|
12
|
+
|
13
|
+
# 使用 CIE76 公式计算颜色的相似程度
|
14
|
+
# 参考:
|
15
|
+
# - [CIELAB](http://en.wikipedia.org/wiki/CIELAB)
|
16
|
+
# - [Color Difference](http://en.wikipedia.org/wiki/Color_difference)
|
17
|
+
def similarity( color1, color2, ignore_lightness: false )
|
18
|
+
l1, a1, b1 = *rgb2lab(color1.to_rgb)
|
19
|
+
l2, a2, b2 = *rgb2lab(color2.to_rgb)
|
20
|
+
Math.sqrt( (ignore_lightness ? 0 : (l1-l2)**2) + (a1-a2)**2 + (b1-b2)**2 )
|
21
|
+
end
|
22
|
+
|
23
|
+
# Public: 判断两种颜色的色系相似程度
|
24
|
+
# 在色盘上越接近,返回值越小
|
25
|
+
#
|
26
|
+
# Returns 色相距离,0-180之间的角度值
|
27
|
+
def hue_similarity( color1, color2 )
|
28
|
+
deg1 = color1.to_hsl.h
|
29
|
+
deg2 = color2.to_hsl.h
|
30
|
+
deg1 += 1 if deg1 < 0
|
31
|
+
deg2 += 1 if deg2 < 0
|
32
|
+
delta = (deg1 - deg2).abs
|
33
|
+
delta = 1 - delta if delta > 0.5
|
34
|
+
delta * 360
|
35
|
+
end
|
36
|
+
|
37
|
+
# 来自 github 上的开源项目 chroma
|
38
|
+
# https://github.com/gka/chroma.js/blob/master/src/conversions/rgb2lab.coffee
|
39
|
+
#
|
40
|
+
# color - RGB颜色
|
41
|
+
#
|
42
|
+
# Returns [l*, a*, b*] 值。
|
43
|
+
# 亮度(l*)的范围是(0-100)
|
44
|
+
def rgb2lab(color)
|
45
|
+
r, g, b = color.r * 255, color.g * 255, color.b * 255
|
46
|
+
r = rgb_xyz r
|
47
|
+
g = rgb_xyz g
|
48
|
+
b = rgb_xyz b
|
49
|
+
x = xyz_lab (0.4124564 * r + 0.3575761 * g + 0.1804375 * b) / X
|
50
|
+
y = xyz_lab (0.2126729 * r + 0.7151522 * g + 0.0721750 * b) / Y
|
51
|
+
z = xyz_lab (0.0193339 * r + 0.1191920 * g + 0.9503041 * b) / Z
|
52
|
+
[116 * y - 16, 500 * (x - y), 200 * (y - z)]
|
53
|
+
end
|
54
|
+
|
55
|
+
def rgb_xyz (r)
|
56
|
+
if (r /= 255) <= 0.04045 then r / 12.92 else ((r + 0.055) / 1.055) ** 2.4 end
|
57
|
+
end
|
58
|
+
|
59
|
+
def xyz_lab (x)
|
60
|
+
if x > 0.008856 then x ** (1.0/3) else 7.787037 * x + 4 / 29 end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Public: 根据背景色,计算适用于显示其上的文字的颜色
|
64
|
+
def readable_textcolor( bg: nil, accent: nil, lock_accent: false)
|
65
|
+
l1, a1, b1 = *rgb2lab(bg)
|
66
|
+
if accent
|
67
|
+
l2, a2, b2 = *rgb2lab(accent)
|
68
|
+
return accent if (l1 - l2).abs > 255 * 0.8
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: ajust accent unless lock_accent
|
72
|
+
# 白色会显得品质高档,因此尽量使用白色
|
73
|
+
if l1 < 75
|
74
|
+
Color::RGB.from_html( '#FFFFFF' )
|
75
|
+
else
|
76
|
+
bg.to_hsl.tap { |c| c.l = 0.1 }.to_rgb
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def pure( color, s: 1, l: 1 )
|
81
|
+
color.to_hsl.tap do |c|
|
82
|
+
if s
|
83
|
+
c.s = s
|
84
|
+
else
|
85
|
+
c.s += (1 - c.s) * 0.382
|
86
|
+
end
|
87
|
+
c.l = l if l
|
88
|
+
end.to_rgb
|
89
|
+
end
|
90
|
+
|
91
|
+
def dither( color )
|
92
|
+
color.to_hsl.tap do |c|
|
93
|
+
if c.s > 0.8
|
94
|
+
c.s -= (c.s) * 0.3
|
95
|
+
c.l -= 0.1
|
96
|
+
else
|
97
|
+
c.s += (1-c.s) * 0.5
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def reverse_color( color )
|
103
|
+
color.to_hsl.tap do |c|
|
104
|
+
# 色盘旋转60度
|
105
|
+
h = c.h + 1.0/6
|
106
|
+
h -= 1 if h > 1
|
107
|
+
c.h = h
|
108
|
+
end.to_rgb
|
109
|
+
end
|
110
|
+
|
111
|
+
def darken( hsl_color, percent )
|
112
|
+
hsl_color.tap { |c| darken!( c, percent ) }
|
113
|
+
end
|
114
|
+
|
115
|
+
def darken!( hsl_color, percent )
|
116
|
+
l = hsl_color.l
|
117
|
+
hsl_color.l -= (1-l) * percent
|
118
|
+
end
|
119
|
+
|
120
|
+
def lighten( hsl_color, percent )
|
121
|
+
hsl_color.tap { |c| lighten!( c, percent ) }
|
122
|
+
end
|
123
|
+
|
124
|
+
def lighten!( hsl_color, percent )
|
125
|
+
l = hsl_color.l
|
126
|
+
hsl_color.l += l * percent
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
module ColorExtract
|
2
|
+
|
3
|
+
class Palette
|
4
|
+
|
5
|
+
include ColorUtil
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def from_colors( colors )
|
9
|
+
new.tap do |palette|
|
10
|
+
palette.raw_colors = colors.to_a
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_accessor :raw_colors, :main_color, :max_pri_brightness
|
16
|
+
|
17
|
+
def initialize( file = nil )
|
18
|
+
@analytics = Analytics.new( file ) if file
|
19
|
+
end
|
20
|
+
|
21
|
+
# 目前只生成一种配色方案
|
22
|
+
def palette( accent_seed: nil )
|
23
|
+
palettes( count: 1, accent_seed: accent_seed ).first
|
24
|
+
end
|
25
|
+
|
26
|
+
def palettes( opts={} )
|
27
|
+
@raw_colors ||= @analytics.valuable_colors.map {|per, c| c }
|
28
|
+
@palettes ||= gen_palettes( opts )
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# 目前采用最简单
|
34
|
+
def gen_palettes( count: 1, accent_seed: nil, max_pri_brightness: 1 )
|
35
|
+
count = 1 if accent_seed
|
36
|
+
@max_pri_brightness = max_pri_brightness
|
37
|
+
@main_color = most_possible_main_color
|
38
|
+
@accent_colors = []
|
39
|
+
|
40
|
+
palettes = count.times.map do |i|
|
41
|
+
gen_palette( accent_seed: accent_seed, fewest_index: i )
|
42
|
+
end.compact.sort_by do |pal|
|
43
|
+
pal[:primary].to_hsl.h
|
44
|
+
end
|
45
|
+
|
46
|
+
if palettes.size < count
|
47
|
+
|
48
|
+
if palettes.empty?
|
49
|
+
return preset_palettes( count: count )
|
50
|
+
end
|
51
|
+
|
52
|
+
last_primary_color = palettes.last[:primary].to_hsl
|
53
|
+
main_color = palettes.last[:back].to_hsl
|
54
|
+
puts last_primary_color.html
|
55
|
+
(count - palettes.size ).times do |i|
|
56
|
+
if i.even?
|
57
|
+
color = last_primary_color.dup.tap do |c|
|
58
|
+
h = c.h
|
59
|
+
h += ((i+1)/2.0).ceil / 10.0
|
60
|
+
h += 1 if h < 0
|
61
|
+
c.h = h
|
62
|
+
c.l = 0.33
|
63
|
+
end.to_rgb
|
64
|
+
else
|
65
|
+
color = main_color.dup.tap do |c|
|
66
|
+
h = c.h
|
67
|
+
h -= ((i+1)/2.0).ceil / 10.0
|
68
|
+
h += 1 if h < 0
|
69
|
+
c.h = h
|
70
|
+
c.l = 0.33
|
71
|
+
end.to_rgb
|
72
|
+
end
|
73
|
+
next if already_have_similar_accent_color?( color )
|
74
|
+
palettes << gen_palette( accent_seed: color )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
palettes.compact
|
79
|
+
end
|
80
|
+
|
81
|
+
def preset_palettes( count: 1 )
|
82
|
+
0.upto(count).map do |i|
|
83
|
+
accent = Color::HSL.from_fraction( i*(1.0 /(count+1)), 0.95, 0.4 )
|
84
|
+
main = Color::RGB.from_html( '#ffffff' )
|
85
|
+
{
|
86
|
+
:primary => accent.to_rgb,
|
87
|
+
:back => main,
|
88
|
+
:"pri-dark" => darken( accent, 0.2 ).to_rgb,
|
89
|
+
:"pri-light" => lighten( accent, 0.2 ).to_rgb,
|
90
|
+
:"text" => readable_textcolor( bg: accent.to_rgb, accent: main.to_rgb )
|
91
|
+
}
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def most_possible_main_color
|
96
|
+
raw_colors.size > 0 ? raw_colors.first : Color::RGB.from_html( '#CCCCCC' )
|
97
|
+
end
|
98
|
+
|
99
|
+
# Private: 生成配色方案
|
100
|
+
#
|
101
|
+
# accent_seed: 指定的 primary 颜色
|
102
|
+
# fewest_index: 指定倒数第几个最少的颜色
|
103
|
+
# 0 - 最少的颜色;
|
104
|
+
# 1 - 倒数第二少的颜色;
|
105
|
+
# 以此类推
|
106
|
+
#
|
107
|
+
# Returns 一种配色方案
|
108
|
+
def gen_palette( accent_seed: nil, fewest_index: 0 )
|
109
|
+
accent_color = accent_seed || most_possible_accent_color( fewest_index )
|
110
|
+
if !accent_color || already_have_similar_accent_color?( accent_color )
|
111
|
+
return nil
|
112
|
+
else
|
113
|
+
@accent_colors << accent_color
|
114
|
+
end
|
115
|
+
|
116
|
+
if accent_color
|
117
|
+
light, dark = most_possible_around_colors( accent_color )
|
118
|
+
text_color = readable_textcolor bg: accent_color, accent: main_color
|
119
|
+
{
|
120
|
+
primary: accent_color,
|
121
|
+
:'pri-light' => light,
|
122
|
+
:'pri-dark' => dark,
|
123
|
+
back: @main_color,
|
124
|
+
text: text_color
|
125
|
+
}
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def most_possible_accent_color( fewest_index=0, &block )
|
130
|
+
|
131
|
+
color = if raw_colors.size > fewest_index + 1
|
132
|
+
# 除去最多的一种颜色
|
133
|
+
# 因为最多的颜色是作为 back
|
134
|
+
raw_colors.reverse[0..-2].compact.sort_by do |c|
|
135
|
+
hue_simi = hue_similarity(c, main_color)
|
136
|
+
simi = hue_simi > 60 ? -0.5*hue_simi + 120 : 1.5*hue_simi
|
137
|
+
l,a,b = *rgb2lab(c)
|
138
|
+
sat = c.to_hsl.s
|
139
|
+
# 按和主要颜色的差异系数由小到大排
|
140
|
+
# 饱和度越高、亮度越低的越有可能成为主色
|
141
|
+
# simi: 0-90; l: 0-100; sat: 0-1
|
142
|
+
simi * 2 + sat * 10 - l * 7.01
|
143
|
+
end.reverse[fewest_index]
|
144
|
+
end
|
145
|
+
|
146
|
+
if max_pri_brightness && color
|
147
|
+
color.to_hsl.tap do |c|
|
148
|
+
c.l = max_pri_brightness if c.l > max_pri_brightness
|
149
|
+
end.to_rgb
|
150
|
+
else
|
151
|
+
color
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def already_have_similar_accent_color?( color )
|
156
|
+
@accent_colors.any? do |c|
|
157
|
+
similarity( c, color, ignore_lightness: false ) < 20
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def most_possible_around_colors( accent_color )
|
162
|
+
sub_color1 = nil
|
163
|
+
sub_color2 = nil
|
164
|
+
|
165
|
+
if raw_colors.size > 4
|
166
|
+
raw_colors[1..-1].each do |c|
|
167
|
+
if hue_similarity(c, accent_color ) < 30
|
168
|
+
if sub_color1.nil?
|
169
|
+
sub_color1 = c.to_hsl.tap do |c|
|
170
|
+
c.l = accent_color.to_hsl.l + 0.2
|
171
|
+
end.to_rgb
|
172
|
+
else
|
173
|
+
sub_color2 = c.to_hsl.tap do |c|
|
174
|
+
c.l = accent_color.to_hsl.l - 0.2
|
175
|
+
end.to_rgb
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
sub_color1 ||= accent_color.to_hsl.tap {|c| lighten!(c, 0.2) }.to_rgb
|
182
|
+
sub_color2 ||= accent_color.to_hsl.tap {|c| darken!(c, 0.2) }.to_rgb
|
183
|
+
[sub_color1, sub_color2]
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: color_extract
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- qhwa
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorscore
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: extract color information from images
|
56
|
+
email:
|
57
|
+
- qhwa@163.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- Gemfile
|
64
|
+
- LICENSE.txt
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- color_extract.gemspec
|
68
|
+
- example/extract.rb
|
69
|
+
- lib/color_extract.rb
|
70
|
+
- lib/color_extract/analytics.rb
|
71
|
+
- lib/color_extract/color_util.rb
|
72
|
+
- lib/color_extract/palette.rb
|
73
|
+
- lib/color_extract/version.rb
|
74
|
+
homepage: https://github.com/qhwa/color_extract
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.2.2
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: extract color information from images
|
98
|
+
test_files: []
|