miro 0.2.3 → 0.3.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8da79a7879154e553ec78af76e25f2a9d8a6b232
4
- data.tar.gz: d180f86a6a43976d2d0ef84455aca46b109e36af
3
+ metadata.gz: 269d3bca169bf72e9496cf0b019b2f39d844f05b
4
+ data.tar.gz: 7ca804e24f8f4433fb01064c98477b3c56ac8804
5
5
  SHA512:
6
- metadata.gz: 85e2a88179149a9afd9e4f62ad5a997115d705c72dd8d649a98b1cf157ee1e12fc756cf006a9f688f6e3a39c3aab8a6c4c03b8679fa2ba5765bb43c4330f0ee5
7
- data.tar.gz: b870d98b936ebc868b49d5771ae7e6a402c640becc21bbd5c84c3abc81e5da488dc37298277c900fe2de9bef94b1c1fd52badf2fa0cfbfd8d8b81fa6000fffd6
6
+ metadata.gz: c2391941d1a5fe148ca76c0807fa78db23ac7b86ff00df2a95b5d420217c06aecc354242dc8b1ae49162cb9cdf3f185b5b50a84d3c46ee44d8c8ee976b077801
7
+ data.tar.gz: 4f9ac4fb9fccf3ad157411175860a62c1dc935ea5b6b8df1e754204d6b58fa730802d4500c4a7c90733b4dce90935eaa91fea27d74fead5817ac0b7d5a5a3c84
@@ -0,0 +1,9 @@
1
+ # Miro Changelog
2
+
3
+ ## 0.3.0
4
+
5
+ * Enhancements
6
+ * new `:histogram` option for improved performance [#19][]
7
+ * Updated README
8
+
9
+ [#19]: https://github.com/jonbuda/miro/pull/19
data/README.md CHANGED
@@ -34,7 +34,9 @@ Miro.options # => {:image_magick_path => "/usr/bin/convert", :resolution => "15
34
34
  Miro.options[:image_magick_path] = '/usr/local/bin/convert'
35
35
  Miro.options[:resolution] = '100x100'
36
36
  Miro.options[:color_count] = 4
37
+ Miro.options[:method] = 'histogram'
37
38
  ```
39
+ By default the method option is configured with 'pixel_group' to improve performance change to 'histogram', this will fetch directly from imagemagick the image histogram without any disk operation(read/write) but read the original file.
38
40
 
39
41
  Initializing an image
40
42
 
@@ -58,8 +60,18 @@ colors.to_rgb # => [[81, 51, 42], [44, 29, 24], [108, 73, 55], [101, 81, 74], [1
58
60
  # RGB with Alpha channels
59
61
  colors.to_rgba # => [[82, 37, 40, 255], [48, 17, 19, 255], [109, 70, 71, 255], [221, 158, 48, 255], [168, 103, 48, 255], [226, 178, 79, 255], [191, 146, 65, 255], [199, 165, 150, 255]]
60
62
  ```
63
+ **Aditional methods only aviable when histogram method is enabled**
61
64
 
62
- Retrieving percentages of colors
65
+ ```ruby
66
+ # HSL
67
+ colors.to_hsl # => [[0.03846153846153848, 0.3170731707317073, 0.24117647058823527], [0.041666666666666664, 0.29411764705882354, 0.13333333333333333], [0.056603773584905634, 0.32515337423312884, 0.3196078431372549], [0.043209876543209826, 0.15428571428571422, 0.3431372549019608], [0.04999999999999999, 0.30701754385964913, 0.4470588235294118], [0.2727272727272729, 0.18644067796610145, 0.884313725490196], [0.030158730158730152, 0.47511312217194573, 0.4333333333333333], [0.17283950617283947, 0.22314049586776857, 0.5254901960784314]]
68
+ # CMYL
69
+ colors.to_cmyk # => [[0.15772788927335635, 0.2753749480968858, 0.3106690657439446, 0.5246250519031143], [0.10680607458669744, 0.16562960399846216, 0.1852374471357171, 0.7206449058054594], [0.18404784313725486, 0.32130274509803924, 0.39189098039215686, 0.3924227450980392], [0.20410654363706265, 0.2825379161860822, 0.30998889657823897, 0.3998150249903884], [0.18299487889273353, 0.37515174163783166, 0.4575046828143022, 0.23269139561707036], [0.11147515570934258, 0.0840241753171857, 0.12716143021914644, 0.010093471741637827], [0.14225937716262976, 0.479514279123414, 0.5540240830449827, 0.21852493656286048], [0.23082082276047672, 0.22297768550557479, 0.43474239138792764, 0.1456497654748174]]
70
+ # YIQ
71
+ colors.to_yiq # => => [[0.23115294117647056, 0.08144705882352937, 0.013964705882352914], [0.129078431372549, 0.04135294117647058, 0.006372549019607841], [0.31926666666666664, 0.10446274509803921, 0.007145098039215689], [0.33796862745098033, 0.05555686274509804, 0.008090196078431366], [0.4402235294117646, 0.14096078431372547, 0.015125490196078437], [0.892756862745098, 0.0, 0.0], [0.3943058823529411, 0.2249215686274509, 0.0483254901960784], [0.6048862745098039, 0.06330196078431369, 0.0]]
72
+ ```
73
+
74
+ Retrieving percentages of colors *disabled on histogram method*
63
75
 
64
76
  ```ruby
65
77
  # percentage of each color in an image. matches sorting from to_hex,
@@ -1,6 +1,7 @@
1
1
  require "miro/version"
2
2
  require "oily_png"
3
3
  require "cocaine"
4
+ require "color"
4
5
  require "tempfile"
5
6
  require "open-uri"
6
7
 
@@ -14,8 +15,16 @@ module Miro
14
15
  :image_magick_path => convert.length > 0 ? convert : '/usr/bin/convert',
15
16
  :resolution => "150x150",
16
17
  :color_count => 8,
17
- :quantize => 'RGB'
18
+ :quantize => 'RGB',
19
+ :method => 'pixel_group'
18
20
  }
19
21
  end
22
+ def pixel_group?
23
+ options[:method] == 'pixel_group'
24
+ end
25
+
26
+ def histogram?
27
+ options[:method] == 'histogram'
28
+ end
20
29
  end
21
30
  end
@@ -8,18 +8,34 @@ module Miro
8
8
  end
9
9
 
10
10
  def to_hex
11
- sorted_pixels.collect {|c| ChunkyPNG::Color.to_hex c, false }
11
+ return histogram.map{ |item| item[1].html } if Miro.histogram?
12
+ sorted_pixels.collect { |pixel| ChunkyPNG::Color.to_hex(pixel, false) }
12
13
  end
13
14
 
14
15
  def to_rgb
15
- sorted_pixels.collect {|c| ChunkyPNG::Color.to_truecolor_bytes c }
16
+ return histogram.map { |item| item[1].to_rgb.to_a } if Miro.histogram?
17
+ sorted_pixels.collect { |pixel| ChunkyPNG::Color.to_truecolor_bytes(pixel) }
16
18
  end
17
19
 
18
20
  def to_rgba
19
- sorted_pixels.collect {|c| ChunkyPNG::Color.to_truecolor_alpha_bytes c }
21
+ return histogram.map { |item| item[1].css_rgba } if Miro.histogram?
22
+ sorted_pixels.collect { |pixel| ChunkyPNG::Color.to_truecolor_alpha_bytes(pixel) }
23
+ end
24
+
25
+ def to_hsl
26
+ histogram.map { |item| item[1].to_hsl.to_a } if Miro.histogram?
27
+ end
28
+
29
+ def to_cmyk
30
+ histogram.map { |item| item[1].to_cmyk.to_a } if Miro.histogram?
31
+ end
32
+
33
+ def to_yiq
34
+ histogram.map { |item| item[1].to_yiq.to_a } if Miro.histogram?
20
35
  end
21
36
 
22
37
  def by_percentage
38
+ return nil if Miro.histogram?
23
39
  sorted_pixels
24
40
  pixel_count = @pixels.size
25
41
  sorted_pixels.collect { |pixel| @grouped_pixels[pixel].size / pixel_count.to_f }
@@ -29,23 +45,40 @@ module Miro
29
45
  @sorted_pixels ||= extract_colors_from_image
30
46
  end
31
47
 
48
+ def histogram
49
+ @histogram || downsample_and_histogram.sort_by { |item| item[0] }.reverse
50
+ end
32
51
  private
52
+
53
+ def downsample_and_histogram
54
+ @source_image = open_source_image
55
+ hstring = Cocaine::CommandLine.new(Miro.options[:image_magick_path], image_magick_params).
56
+ run(:in => File.expand_path(@source_image.path),
57
+ :resolution => Miro.options[:resolution],
58
+ :colors => Miro.options[:color_count].to_s,
59
+ :quantize => Miro.options[:quantize])
60
+ cleanup_temporary_files!
61
+ parse_result(hstring)
62
+ end
63
+
64
+ def parse_result(hstring)
65
+ hstring.scan(/(\d*):.*(#[0-9A-Fa-f]*)/).collect do |match|
66
+ [match[0].to_i, Object.const_get("Color::#{Miro.options[:quantize].upcase}").from_html(match[1])]
67
+ end
68
+ end
69
+
33
70
  def extract_colors_from_image
34
71
  downsample_colors_and_convert_to_png!
35
72
  colors = sort_by_dominant_color
36
73
  cleanup_temporary_files!
37
- return colors
38
- end
39
-
40
- def remote_source_image?
41
- @src_image_path =~ /^https?:\/\//
74
+ colors
42
75
  end
43
76
 
44
77
  def downsample_colors_and_convert_to_png!
45
78
  @source_image = open_source_image
46
79
  @downsampled_image = open_downsampled_image
47
80
 
48
- Cocaine::CommandLine.new(Miro.options[:image_magick_path], "':in[0]' -resize :resolution -colors :colors -colorspace :quantize -quantize :quantize :out").
81
+ Cocaine::CommandLine.new(Miro.options[:image_magick_path], image_magick_params).
49
82
  run(:in => File.expand_path(@source_image.path),
50
83
  :resolution => Miro.options[:resolution],
51
84
  :colors => Miro.options[:color_count].to_s,
@@ -54,22 +87,20 @@ module Miro
54
87
  end
55
88
 
56
89
  def open_source_image
57
- if remote_source_image?
58
- original_extension = @image_type || URI.parse(@src_image_path).path.split('.').last
90
+ return File.open(@src_image_path) unless remote_source_image?
59
91
 
60
- tempfile = Tempfile.open(["source", ".#{original_extension}"])
61
- remote_file_data = open(@src_image_path).read
92
+ original_extension = @image_type || URI.parse(@src_image_path).path.split('.').last
62
93
 
63
- tempfile.write(should_force_encoding? ? remote_file_data.force_encoding("UTF-8") : remote_file_data)
64
- tempfile.close
65
- return tempfile
66
- else
67
- return File.open(@src_image_path)
68
- end
94
+ tempfile = Tempfile.open(["source", ".#{original_extension}"])
95
+ remote_file_data = open(@src_image_path).read
96
+
97
+ tempfile.write(should_force_encoding? ? remote_file_data.force_encoding("UTF-8") : remote_file_data)
98
+ tempfile.close
99
+ tempfile
69
100
  end
70
101
 
71
102
  def should_force_encoding?
72
- Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('1.9')
103
+ Gem::Version.new(RUBY_VERSION.dup) >= Gem::Version.new('1.9')
73
104
  end
74
105
 
75
106
  def open_downsampled_image
@@ -78,6 +109,14 @@ module Miro
78
109
  tempfile
79
110
  end
80
111
 
112
+ def image_magick_params
113
+ if Miro.histogram?
114
+ "':in[0]' -resize :resolution -colors :colors -colorspace :quantize -quantize :quantize -alpha remove -format %c histogram:info:"
115
+ else
116
+ "':in[0]' -resize :resolution -colors :colors -colorspace :quantize -quantize :quantize :out"
117
+ end
118
+ end
119
+
81
120
  def group_pixels_by_color
82
121
  @pixels ||= ChunkyPNG::Image.from_file(File.expand_path(@downsampled_image.path)).pixels
83
122
  @grouped_pixels ||= @pixels.group_by { |pixel| pixel }
@@ -88,8 +127,12 @@ module Miro
88
127
  end
89
128
 
90
129
  def cleanup_temporary_files!
91
- @source_image.close(true) if remote_source_image?
92
- @downsampled_image.close(true)
130
+ @source_image.close! if remote_source_image?
131
+ @downsampled_image.close! if @downsampled_image
132
+ end
133
+
134
+ def remote_source_image?
135
+ @src_image_path =~ /^https?:\/\//
93
136
  end
94
137
  end
95
138
  end
@@ -1,3 +1,3 @@
1
1
  module Miro
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -12,6 +12,7 @@ Gem::Specification.new do |gem|
12
12
 
13
13
  gem.add_dependency "cocaine"
14
14
  gem.add_dependency "oily_png"
15
+ gem.add_dependency "color"
15
16
 
16
17
  gem.add_development_dependency "rspec"
17
18
  gem.add_development_dependency "fakeweb"
@@ -22,4 +23,5 @@ Gem::Specification.new do |gem|
22
23
  gem.name = "miro"
23
24
  gem.require_paths = ["lib"]
24
25
  gem.version = Miro::VERSION
26
+ gem.license = "MIT"
25
27
  end
Binary file
@@ -1,10 +1,11 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Miro::DominantColors do
4
+ context "colors_group method" do
4
5
  let(:subject) { Miro::DominantColors.new('/path/to/file') }
5
6
  let(:mock_source_image) { double('file', :path => '/path/to/source_image').as_null_object }
6
7
  let(:mock_downsampled_image) { double('file', :path => '/path/to/downsampled_image').as_null_object }
7
- let(:pixel_data) {
8
+ let(:pixel_data) {
8
9
  [ 2678156287,
9
10
  1362307839, 1362307839, 1362307839, 1362307839, 1362307839, 1362307839, 1362307839, 1362307839,
10
11
  2506379263, 2506379263, 2506379263, 2506379263,
@@ -20,6 +21,7 @@ describe Miro::DominantColors do
20
21
  let(:expected_rgb_values) { [[81, 51, 42], [44, 29, 24], [108, 73, 55], [101, 81, 74], [149, 100, 79], [224, 231, 220], [163, 77, 58], [159, 161, 107]] }
21
22
 
22
23
  before do
24
+ Miro.stub(:histogram?).and_return(false)
23
25
  Cocaine::CommandLine.stub(:new).and_return(double('command', :run => true))
24
26
  ChunkyPNG::Image.stub(:from_file).and_return(chunky_png_results)
25
27
  end
@@ -88,8 +90,8 @@ describe Miro::DominantColors do
88
90
  end
89
91
 
90
92
  it "deletes only temporary downsampled file when finished" do
91
- mock_downsampled_image.should_receive(:close).with(true)
92
- mock_source_image.should_not_receive(:close)
93
+ mock_downsampled_image.should_receive(:close!)
94
+ mock_source_image.should_not_receive(:close!)
93
95
  subject.sorted_pixels
94
96
  end
95
97
  end
@@ -108,7 +110,7 @@ describe Miro::DominantColors do
108
110
  end
109
111
 
110
112
  it "deletes the temporary source file when finished" do
111
- mock_source_image.should_receive(:close).with(true)
113
+ mock_source_image.should_receive(:close!)
112
114
  subject.sorted_pixels
113
115
  end
114
116
  end
@@ -155,5 +157,88 @@ describe Miro::DominantColors do
155
157
  end
156
158
  end
157
159
  end
158
-
160
+ end
161
+
162
+ context "histogram method" do
163
+ let(:subject) { Miro::DominantColors.new(File.expand_path('spec/data/test.png')) }
164
+ let(:hex_colors){["#00ff02","#0000ff","#ff009a","#fa0000","#878787","#585858","#001a6b","#8f0074"]}
165
+ let(:object_colors){ hex_colors.map{|c| Color::RGB.from_html(c) } }
166
+
167
+ before do
168
+ Miro.stub(:histogram?).and_return(true)
169
+ end
170
+
171
+ describe "#downsample_and_histogram" do
172
+ it "should return an array" do
173
+ subject.send(:downsample_and_histogram).should be_an_instance_of(Array)
174
+ end
175
+
176
+ it "should contain colors" do
177
+ subject.send(:downsample_and_histogram).each do |item|
178
+ item[1].should be_an_instance_of(Color::RGB)
179
+ end
180
+ end
181
+
182
+ it "should have the max length of the color config" do
183
+ subject.send(:downsample_and_histogram).count.should <= Miro.options[:color_count]
184
+ end
185
+ end
186
+
187
+ describe "#histogram" do
188
+ it "should return a hash" do
189
+ subject.histogram.should be_an_instance_of(Array)
190
+ end
191
+
192
+ it "should start with the most used color" do
193
+ subject.histogram.first[1].should == Color::RGB.from_html('#00FF02')
194
+ end
195
+
196
+ it "should end with the less used color" do
197
+ subject.histogram.last[1].should == Color::RGB.from_html('#8F0074')
198
+ end
199
+ end
200
+
201
+ describe "#to_hex" do
202
+ it "should be #00FF02 at the first element" do
203
+ subject.to_hex.first.should == hex_colors.first
204
+ end
205
+ it "should be #8F0074 at the first element" do
206
+ subject.to_hex.last.should == hex_colors.last
207
+ end
208
+ it "should have the right values" do
209
+ subject.to_hex.should == hex_colors
210
+ end
211
+ end
212
+
213
+ describe "#to_rgb" do
214
+ it "should have the right values" do
215
+ subject.to_rgb.should == object_colors.map(&:to_rgb).map(&:to_a)
216
+ end
217
+ end
218
+
219
+ describe "#to_rgba" do
220
+ it "should have the right values" do
221
+ subject.to_rgba.should == object_colors.map(&:css_rgba)
222
+ end
223
+ end
224
+
225
+ describe "#to_hsl" do
226
+ it "should have the right values" do
227
+ subject.to_hsl.should == object_colors.map(&:to_hsl).map(&:to_a)
228
+ end
229
+ end
230
+
231
+ describe "#to_cmyk" do
232
+ it "should have the right values" do
233
+ subject.to_cmyk.should == object_colors.map(&:to_cmyk).map(&:to_a)
234
+ end
235
+ end
236
+
237
+ describe "#to_yiq" do
238
+ it "should have the right values" do
239
+ subject.to_yiq.should == object_colors.map(&:to_yiq).map(&:to_a)
240
+ end
241
+ end
242
+
243
+ end
159
244
  end
metadata CHANGED
@@ -1,69 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Buda
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-18 00:00:00.000000000 Z
11
+ date: 2014-04-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: cocaine
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: oily_png
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: '0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: color
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rspec
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - '>='
59
+ - - ">="
46
60
  - !ruby/object:Gem::Version
47
61
  version: '0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
- - - '>='
66
+ - - ">="
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: fakeweb
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
- - - '>='
73
+ - - ">="
60
74
  - !ruby/object:Gem::Version
61
75
  version: '0'
62
76
  type: :development
63
77
  prerelease: false
64
78
  version_requirements: !ruby/object:Gem::Requirement
65
79
  requirements:
66
- - - '>='
80
+ - - ">="
67
81
  - !ruby/object:Gem::Version
68
82
  version: '0'
69
83
  description: Extract the dominant colors from an image.
@@ -73,8 +87,9 @@ executables: []
73
87
  extensions: []
74
88
  extra_rdoc_files: []
75
89
  files:
76
- - .gitignore
77
- - .rspec
90
+ - ".gitignore"
91
+ - ".rspec"
92
+ - CHANGELOG.md
78
93
  - Gemfile
79
94
  - LICENSE
80
95
  - README.md
@@ -83,11 +98,13 @@ files:
83
98
  - lib/miro/dominant_colors.rb
84
99
  - lib/miro/version.rb
85
100
  - miro.gemspec
101
+ - spec/data/test.png
86
102
  - spec/miro/dominant_colors_spec.rb
87
103
  - spec/miro_spec.rb
88
104
  - spec/spec_helper.rb
89
105
  homepage: https://github.com/jonbuda/miro
90
- licenses: []
106
+ licenses:
107
+ - MIT
91
108
  metadata: {}
92
109
  post_install_message:
93
110
  rdoc_options: []
@@ -95,22 +112,23 @@ require_paths:
95
112
  - lib
96
113
  required_ruby_version: !ruby/object:Gem::Requirement
97
114
  requirements:
98
- - - '>='
115
+ - - ">="
99
116
  - !ruby/object:Gem::Version
100
117
  version: '0'
101
118
  required_rubygems_version: !ruby/object:Gem::Requirement
102
119
  requirements:
103
- - - '>='
120
+ - - ">="
104
121
  - !ruby/object:Gem::Version
105
122
  version: '0'
106
123
  requirements:
107
124
  - ImageMagick
108
125
  rubyforge_project:
109
- rubygems_version: 2.0.3
126
+ rubygems_version: 2.2.2
110
127
  signing_key:
111
128
  specification_version: 4
112
129
  summary: Extract the dominant colors from an image.
113
130
  test_files:
131
+ - spec/data/test.png
114
132
  - spec/miro/dominant_colors_spec.rb
115
133
  - spec/miro_spec.rb
116
134
  - spec/spec_helper.rb