camalian 0.1.0 → 0.1.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 +4 -4
- data/.rubocop.yml +4 -0
- data/README.md +38 -5
- data/lib/camalian.rb +3 -1
- data/lib/camalian/color.rb +20 -9
- data/lib/camalian/image.rb +1 -1
- data/lib/camalian/palette.rb +4 -0
- data/lib/camalian/quantization/histogram.rb +1 -1
- data/lib/camalian/quantization/k_means.rb +59 -0
- data/lib/camalian/version.rb +1 -1
- data/test/test_helper.rb +4 -0
- data/test/test_quantization.rb +47 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01c8d4bae81e85284d554a538c52e90741f0db58ddba1d6d9e921761cf041d91
|
4
|
+
data.tar.gz: c5a301ed8505a08e31ed1c65cc4e5c8b0b1ccc832fad6e8279cb279f4750dadd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0418f17dfb07d45db856e30e9b333b532be7a38045aa4be3aae32192e62de4c803837a6730df08a797f3c662bd9a3fc0f21a5e780edfcf8545b3c085440af2d0'
|
7
|
+
data.tar.gz: 0e1144ff5d5d9a8faef915c23518759b98bebd6efdb6a4b39ea7a11ecd67d63e07847b05982853a20b0a719a855d16a1dcba45f28ac97383478a319ef9281488
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Ruby gem to extract color palettes from images and play with their saturation
|
|
10
10
|
|
11
11
|
Add this line to your application's Gemfile:
|
12
12
|
|
13
|
-
gem 'camalian', '~> 0.1.
|
13
|
+
gem 'camalian', '~> 0.1.1'
|
14
14
|
|
15
15
|
And then execute:
|
16
16
|
|
@@ -22,10 +22,43 @@ Or install it yourself as:
|
|
22
22
|
|
23
23
|
## Usage
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
```ruby
|
26
|
+
image = Camalian::load('file_path')
|
27
|
+
colors = image.prominent_colors(15)
|
28
|
+
colors = colors.sort_similar_colors
|
29
|
+
colors.light_colors(0, 40)
|
30
|
+
```
|
31
|
+
|
32
|
+
### Quantization Algorithms
|
33
|
+
Currently following algorithms are implemented.
|
34
|
+
|
35
|
+
**Histogram**
|
36
|
+
|
37
|
+
Its a most common algorithm for color quantization and used different bucket technique to group the colors together. You can read more about this technique here https://en.wikipedia.org/wiki/Color_histogram. It can be accessed by `Camalian::QUANTIZATION_HISTOGRAM` constant. This is used as default method as well.
|
38
|
+
|
39
|
+
|
40
|
+
**K Means**
|
41
|
+
|
42
|
+
This algorithm uses color distancing in RGB space to group the similar colors. You can learn more about this technique here https://en.wikipedia.org/wiki/K-means_clustering. It can be accessed by `Camalian::QUANTIZATION_K_MEANS` constant.
|
43
|
+
|
44
|
+
|
45
|
+
You can set default quantization method globally as:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
Camalian.options[:quantization] = Camalian::QUANTIZATION_K_MEANS
|
49
|
+
```
|
50
|
+
|
51
|
+
or you can set at the time of extracting colors by.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
image = Camalian::load('file_path')
|
55
|
+
colors = image.prominent_colors(15, quantization: Camalian::QUANTIZATION_K_MEANS)
|
56
|
+
```
|
57
|
+
|
58
|
+
## Usage
|
59
|
+
You can find a working example with detail explanation and reference code here on [this link](https://basicdrift.com/color-extraction-library-build-color-search-engine-fdf369678d5a). Here we will build a functional color based image search engine in Ruby on Rails.
|
60
|
+
|
61
|
+
NOTE: Since its a compute intensive operation so for production use its suggested to use under a background job and not within a request/response cycle.
|
29
62
|
|
30
63
|
## Contributing
|
31
64
|
|
data/lib/camalian.rb
CHANGED
@@ -9,9 +9,11 @@ require 'camalian/color'
|
|
9
9
|
require 'camalian/palette'
|
10
10
|
require 'camalian/image'
|
11
11
|
require 'camalian/quantization/histogram'
|
12
|
+
require 'camalian/quantization/k_means'
|
12
13
|
|
13
14
|
module Camalian # :nodoc:
|
14
|
-
QUANTIZATION_HISTOGRAM =
|
15
|
+
QUANTIZATION_HISTOGRAM = Camalian::Quantization::Histogram
|
16
|
+
QUANTIZATION_K_MEANS = Camalian::Quantization::KMeans
|
15
17
|
|
16
18
|
class << self
|
17
19
|
def options
|
data/lib/camalian/color.rb
CHANGED
@@ -10,7 +10,12 @@ module Camalian
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.from_hex(hex_value)
|
13
|
-
|
13
|
+
color_hash = hex_value[0..6]
|
14
|
+
color_hash = color_hash[1..6] if color_hash[0] == '#'
|
15
|
+
r = color_hash[0..1].to_i(16)
|
16
|
+
g = color_hash[2..3].to_i(16)
|
17
|
+
b = color_hash[4..5].to_i(16)
|
18
|
+
|
14
19
|
Color.new(r, g, b)
|
15
20
|
end
|
16
21
|
|
@@ -22,17 +27,23 @@ module Camalian
|
|
22
27
|
"##{r.to_s(16).rjust(2, '0')}#{g.to_s(16).rjust(2, '0')}#{b.to_s(16).rjust(2, '0')}"
|
23
28
|
end
|
24
29
|
|
25
|
-
|
30
|
+
# Used for array uniqueness
|
31
|
+
def hash
|
32
|
+
"#{r}#{g}#{b}".to_i
|
33
|
+
end
|
34
|
+
|
35
|
+
# Used for object comparison
|
36
|
+
def ==(other)
|
37
|
+
r == other.r && g == other.g && b == other.b
|
38
|
+
end
|
39
|
+
alias eql? ==
|
40
|
+
|
41
|
+
def hue_distance(color)
|
26
42
|
[(h - color.h) % 360, (color.h - h) % 360].min
|
27
43
|
end
|
28
44
|
|
29
|
-
def
|
30
|
-
|
31
|
-
color_hash = color_hash[1..6] if color_hash[0] == '#'
|
32
|
-
r = color_hash[0..1].to_i(16)
|
33
|
-
g = color_hash[2..3].to_i(16)
|
34
|
-
b = color_hash[4..5].to_i(16)
|
35
|
-
[r, g, b]
|
45
|
+
def rgb_distance(color)
|
46
|
+
Math.sqrt(((r - color.r)**2) + ((g - color.g)**2) + ((b - color.b)**2))
|
36
47
|
end
|
37
48
|
|
38
49
|
private
|
data/lib/camalian/image.rb
CHANGED
data/lib/camalian/palette.rb
CHANGED
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Camalian
|
4
|
+
module Quantization
|
5
|
+
class KMeans # :nodoc:
|
6
|
+
INIT_TRIES = 10
|
7
|
+
|
8
|
+
def process(colors, count)
|
9
|
+
# Its faster to extract unique colors once
|
10
|
+
colors = colors.uniq
|
11
|
+
means = initial_means(colors, count)
|
12
|
+
|
13
|
+
done = false
|
14
|
+
|
15
|
+
until done
|
16
|
+
groups = group_by_means(colors, means)
|
17
|
+
new_means = groups.map do |group|
|
18
|
+
Palette.new(group).average_color
|
19
|
+
end
|
20
|
+
common = means & new_means
|
21
|
+
|
22
|
+
done = common.size == means.size
|
23
|
+
means = new_means
|
24
|
+
end
|
25
|
+
|
26
|
+
Palette.new(means)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def initial_means(colors, count)
|
32
|
+
count = colors.size if count > colors.size
|
33
|
+
means = []
|
34
|
+
tries = 0
|
35
|
+
while means.size != count && tries < INIT_TRIES
|
36
|
+
(count - means.size).times { means << colors[rand(colors.size)] }
|
37
|
+
means.uniq!
|
38
|
+
tries += 1
|
39
|
+
end
|
40
|
+
|
41
|
+
means
|
42
|
+
end
|
43
|
+
|
44
|
+
def group_by_means(pixels, means)
|
45
|
+
groups = {}
|
46
|
+
|
47
|
+
pixels.each do |p|
|
48
|
+
distances = means.map { |m| p.rgb_distance(m) }
|
49
|
+
min_distance_index = distances.index(distances.min)
|
50
|
+
|
51
|
+
groups[min_distance_index] ||= []
|
52
|
+
groups[min_distance_index] << p
|
53
|
+
end
|
54
|
+
|
55
|
+
groups.values
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/camalian/version.rb
CHANGED
data/test/test_helper.rb
CHANGED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
describe Camalian::Quantization do
|
6
|
+
[Camalian::Quantization::KMeans, Camalian::Quantization::Histogram].each do |klass|
|
7
|
+
describe klass.name.to_s do
|
8
|
+
it 'should extract distinct colors' do
|
9
|
+
colors = %w[#FF0000 #00FF00 #0000FF].map { |c| Camalian::Color.from_hex(c) }
|
10
|
+
result = klass.new.process(colors, 3)
|
11
|
+
|
12
|
+
_(result.size).must_equal 3
|
13
|
+
_(result).must_equal colors
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should extract distinct colors lesser than pixels' do
|
17
|
+
colors = %w[#FF0000 #00FF00 #0000FF].map { |c| Camalian::Color.from_hex(c) }
|
18
|
+
result = klass.new.process(colors, 2)
|
19
|
+
|
20
|
+
_(result.size).must_equal 2
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should extract distinct colors not more than pixels' do
|
24
|
+
colors = %w[#FF0000 #00FF00 #0000FF].map { |c| Camalian::Color.from_hex(c) }
|
25
|
+
result = klass.new.process(colors, 4)
|
26
|
+
|
27
|
+
_(result.size).must_equal 3
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should extract same color' do
|
31
|
+
colors = %w[#FF0000 #FF0000 #FF0000].map { |c| Camalian::Color.from_hex(c) }
|
32
|
+
result = klass.new.process(colors, 3)
|
33
|
+
|
34
|
+
_(result.size).must_equal 1
|
35
|
+
_(result.to_hex).must_equal ['#ff0000']
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should extract multiple colors' do
|
39
|
+
colors = %w[#FF0000 #FF0000 #00FF00 #0000FF].map { |c| Camalian::Color.from_hex(c) }
|
40
|
+
result = klass.new.process(colors, 3)
|
41
|
+
|
42
|
+
_(result.size).must_equal 3
|
43
|
+
_(result.to_hex).must_equal %w[#ff0000 #00ff00 #0000ff]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: camalian
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nazar Hussain
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-11-
|
11
|
+
date: 2020-11-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chunky_png
|
@@ -95,11 +95,13 @@ files:
|
|
95
95
|
- lib/camalian/image.rb
|
96
96
|
- lib/camalian/palette.rb
|
97
97
|
- lib/camalian/quantization/histogram.rb
|
98
|
+
- lib/camalian/quantization/k_means.rb
|
98
99
|
- lib/camalian/version.rb
|
99
100
|
- test/assets/palette.png
|
100
101
|
- test/test_color.rb
|
101
102
|
- test/test_helper.rb
|
102
103
|
- test/test_palette.rb
|
104
|
+
- test/test_quantization.rb
|
103
105
|
homepage: https://github.com/nazarhussain/camalian
|
104
106
|
licenses:
|
105
107
|
- MIT
|
@@ -129,3 +131,4 @@ test_files:
|
|
129
131
|
- test/test_color.rb
|
130
132
|
- test/test_helper.rb
|
131
133
|
- test/test_palette.rb
|
134
|
+
- test/test_quantization.rb
|