davinci 0.2.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.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in miro.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jon Buda
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.
@@ -0,0 +1,81 @@
1
+ # Miro
2
+
3
+ Extract the dominant colors from an image.
4
+
5
+ ## Feedback
6
+
7
+ Questions, Comments, Concerns, or Bugs?
8
+
9
+ [GitHub Issues For All!](https://github.com/jonbuda/miro/issues)
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'miro'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install miro
24
+
25
+ ## Usage
26
+
27
+ Configuration
28
+
29
+ ```ruby
30
+ # Defaults
31
+ Miro.options # => {:image_magick_path => "/usr/bin/convert", :resolution => "150x150", :color_count => 8}
32
+
33
+ # Setting new options
34
+ Miro.options[:image_magick_path] = '/usr/local/bin/convert'
35
+ Miro.options[:resolution] = '100x100'
36
+ Miro.options[:color_count] = 4
37
+ ```
38
+
39
+ Initializing an image
40
+
41
+ ```ruby
42
+ # Initialize with a local image
43
+ colors = Miro::DominantColors.new('/path/to/local/image.jpg')
44
+
45
+ # or Initialize with a remote image
46
+ colors = Miro::DominantColors.new('http://domain.com/path/to/image.jpg')
47
+ ```
48
+
49
+ Retrieving colors from an image
50
+
51
+ ```ruby
52
+ # Hex
53
+ colors.to_hex # => ["#51332a", "#2c1d18", "#6c4937", "#65514a", "#95644f", "#e0e7dc", "#a34d3a", "#9fa16b"]
54
+
55
+ # RGB
56
+ colors.to_rgb # => [[81, 51, 42], [44, 29, 24], [108, 73, 55], [101, 81, 74], [149, 100, 79], [224, 231, 220], [163, 77, 58], [159, 161, 107]]
57
+
58
+ # RGB with Alpha channels
59
+ 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
+ ```
61
+
62
+ Retrieving percentages of colors
63
+
64
+ ```ruby
65
+ # percentage of each color in an image. matches sorting from to_hex,
66
+ # to_rgb and to_rgba.
67
+
68
+ colors.by_percentage # => [0.50, 0.25, 0.15, 0.10]
69
+ ```
70
+
71
+ ## Contributing
72
+
73
+ 1. Fork it
74
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
75
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
76
+ 4. Push to the branch (`git push origin my-new-feature`)
77
+ 5. Create new Pull Request
78
+
79
+ ## License
80
+
81
+ Copyright 2012 Jon Buda. This is free software, and may be redistributed under the terms specified in the LICENSE file.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,19 @@
1
+ require "miro/version"
2
+ require "oily_png"
3
+ require "cocaine"
4
+ require "tempfile"
5
+ require "open-uri"
6
+
7
+ require "miro/dominant_colors"
8
+
9
+ module Miro
10
+ class << self
11
+ def options
12
+ @options ||= {
13
+ :image_magick_path => "/usr/bin/convert",
14
+ :resolution => "150x150",
15
+ :color_count => 8
16
+ }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,87 @@
1
+ module Miro
2
+ class DominantColors
3
+ attr_accessor :src_image_path
4
+
5
+ def initialize(src_image_path)
6
+ @src_image_path = src_image_path
7
+ end
8
+
9
+ def to_hex
10
+ sorted_pixels.collect {|c| ChunkyPNG::Color.to_hex c, false }
11
+ end
12
+
13
+ def to_rgb
14
+ sorted_pixels.collect {|c| ChunkyPNG::Color.to_truecolor_bytes c }
15
+ end
16
+
17
+ def to_rgba
18
+ sorted_pixels.collect {|c| ChunkyPNG::Color.to_truecolor_alpha_bytes c }
19
+ end
20
+
21
+ def by_percentage
22
+ sorted_pixels
23
+ pixel_count = @pixels.size
24
+ sorted_pixels.collect { |pixel| @grouped_pixels[pixel].size / pixel_count.to_f }
25
+ end
26
+
27
+ def sorted_pixels
28
+ @sorted_pixels ||= extract_colors_from_image
29
+ end
30
+
31
+ private
32
+ def extract_colors_from_image
33
+ downsample_colors_and_convert_to_png!
34
+ colors = sort_by_dominant_color
35
+ cleanup_temporary_files!
36
+ return colors
37
+ end
38
+
39
+ def remote_source_image?
40
+ @src_image_path =~ /^https?:\/\//
41
+ end
42
+
43
+ def downsample_colors_and_convert_to_png!
44
+ @source_image = open_source_image
45
+ @downsampled_image = open_downsampled_image
46
+ Cocaine::CommandLine.new(Miro.options[:image_magick_path], ":in -resize :resolution -colors :colors :out").
47
+ run(:in => File.expand_path(@source_image.path),
48
+ :resolution => Miro.options[:resolution],
49
+ :colors => Miro.options[:color_count].to_s,
50
+ :out => File.expand_path(@downsampled_image.path))
51
+ end
52
+
53
+ def open_source_image
54
+ if remote_source_image?
55
+ original_extension = URI.parse(@src_image_path).path.split('.').last
56
+
57
+ tempfile = Tempfile.open(["source", ".#{original_extension}"])
58
+ remote_file_data = open(@src_image_path).read
59
+
60
+ tempfile.write(RUBY_VERSION =~ /1.9/ ? remote_file_data.force_encoding("UTF-8") : remote_file_data)
61
+ return tempfile
62
+ else
63
+ return File.open(@src_image_path)
64
+ end
65
+ end
66
+
67
+ def open_downsampled_image
68
+ tempfile = Tempfile.open(["downsampled", '.png'])
69
+ tempfile.binmode
70
+ tempfile
71
+ end
72
+
73
+ def group_pixels_by_color
74
+ @pixels ||= ChunkyPNG::Image.from_file(File.expand_path(@downsampled_image.path)).pixels
75
+ @grouped_pixels ||= @pixels.group_by { |pixel| pixel }
76
+ end
77
+
78
+ def sort_by_dominant_color
79
+ group_pixels_by_color.sort_by { |k,v| v.size }.reverse.flatten.uniq
80
+ end
81
+
82
+ def cleanup_temporary_files!
83
+ @source_image.close(true) if remote_source_image?
84
+ @downsampled_image.close(true)
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,3 @@
1
+ module Miro
2
+ VERSION = "0.2.1"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/miro/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Jon Buda"]
6
+ gem.email = ["jon.buda@gmail.com"]
7
+ gem.description = %q{Extract the dominant colors from an image.}
8
+ gem.summary = %q{Extract the dominant colors from an image.}
9
+ gem.homepage = "https://github.com/jonbuda/miro"
10
+
11
+ gem.requirements = 'ImageMagick'
12
+
13
+ gem.add_dependency "cocaine"
14
+ gem.add_dependency "oily_png"
15
+
16
+ gem.add_development_dependency "rspec"
17
+ gem.add_development_dependency "fakeweb"
18
+
19
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ gem.files = `git ls-files`.split("\n")
21
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
22
+ gem.name = "davinci"
23
+ gem.require_paths = ["lib"]
24
+ gem.version = Miro::VERSION
25
+ end
@@ -0,0 +1,157 @@
1
+ require 'spec_helper'
2
+
3
+ describe Miro::DominantColors do
4
+ let(:subject) { Miro::DominantColors.new('/path/to/file') }
5
+ let(:mock_source_image) { double('file', :path => '/path/to/source_image').as_null_object }
6
+ let(:mock_downsampled_image) { double('file', :path => '/path/to/downsampled_image').as_null_object }
7
+ let(:pixel_data) {
8
+ [ 2678156287,
9
+ 1362307839, 1362307839, 1362307839, 1362307839, 1362307839, 1362307839, 1362307839, 1362307839,
10
+ 2506379263, 2506379263, 2506379263, 2506379263,
11
+ 2739747583, 2739747583,
12
+ 1816737791, 1816737791, 1816737791, 1816737791, 1816737791, 1816737791,
13
+ 1699826431, 1699826431, 1699826431, 1699826431, 1699826431,
14
+ 3773291775, 3773291775, 3773291775,
15
+ 740104447, 740104447, 740104447, 740104447, 740104447, 740104447, 740104447 ] }
16
+ let(:chunky_png_results) { double('chunkypng', :pixels => pixel_data) }
17
+ let(:expected_pixels) { [1362307839, 740104447, 1816737791, 1699826431, 2506379263, 3773291775, 2739747583, 2678156287] }
18
+ let(:expected_hex_values) { ["#51332a", "#2c1d18", "#6c4937", "#65514a", "#95644f", "#e0e7dc", "#a34d3a", "#9fa16b"] }
19
+ let(:expected_rgba_values) { [[81, 51, 42, 255], [44, 29, 24, 255], [108, 73, 55, 255], [101, 81, 74, 255], [149, 100, 79, 255], [224, 231, 220, 255], [163, 77, 58, 255], [159, 161, 107, 255]] }
20
+ 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
+ before do
23
+ Cocaine::CommandLine.stub(:new).and_return(double('command', :run => true))
24
+ ChunkyPNG::Image.stub(:from_file).and_return(chunky_png_results)
25
+ end
26
+
27
+ it "takes a paramter and sets the source image path" do
28
+ miro = Miro::DominantColors.new('path/to/file')
29
+ miro.src_image_path.should eq('path/to/file')
30
+ end
31
+
32
+ describe "#sorted_pixels" do
33
+ before { subject.stub(:extract_colors_from_image).and_return(expected_pixels) }
34
+
35
+ it "extracts colors from the image the first time it's called" do
36
+ subject.should_receive(:extract_colors_from_image).and_return(expected_pixels)
37
+ subject.sorted_pixels
38
+ end
39
+
40
+ it "returns previously extracted colors on subsequent calls" do
41
+ subject.should_receive(:extract_colors_from_image).once
42
+ subject.sorted_pixels
43
+ subject.sorted_pixels
44
+ end
45
+ end
46
+
47
+ context "when extracting colors" do
48
+ before do
49
+ File.stub(:open).and_return(mock_source_image)
50
+ end
51
+
52
+ it "opens the tempfile for the downsampled image" do
53
+ Tempfile.should_receive(:open).with(['downsampled', '.png']).and_return(mock_downsampled_image)
54
+ subject.sorted_pixels
55
+ end
56
+
57
+ it "runs the imagemagick command line with the correct arguments" do
58
+ subject.stub(:open_downsampled_image).and_return(mock_downsampled_image)
59
+ line = double('line')
60
+ Cocaine::CommandLine.should_receive(:new).with(Miro.options[:image_magick_path],
61
+ ":in -resize :resolution -colors :colors :out").and_return(line)
62
+ line.should_receive(:run).with(:in => '/path/to/source_image',
63
+ :resolution => Miro.options[:resolution],
64
+ :colors => Miro.options[:color_count].to_s,
65
+ :out => '/path/to/downsampled_image')
66
+ subject.sorted_pixels
67
+ end
68
+
69
+ it "sorts the colors from most dominant to least" do
70
+ subject.stub(:open_downsampled_image).and_return(mock_downsampled_image)
71
+ subject.sorted_pixels.should eq(expected_pixels)
72
+ end
73
+ end
74
+
75
+ context "when the source image is a local image file" do
76
+ let(:subject) { Miro::DominantColors.new('/path/to/image.jpg') }
77
+
78
+ before do
79
+ File.stub(:open).and_return(mock_source_image)
80
+ subject.stub(:open_downsampled_image).and_return(mock_downsampled_image)
81
+ end
82
+
83
+ it "opens for the file resource" do
84
+ File.should_receive(:open).with(subject.src_image_path)
85
+ subject.sorted_pixels
86
+ end
87
+
88
+ it "deletes only temporary downsampled file when finished" do
89
+ mock_downsampled_image.should_receive(:close).with(true)
90
+ mock_source_image.should_not_receive(:close)
91
+ subject.sorted_pixels
92
+ end
93
+ end
94
+
95
+ context "when the source image is a remote image" do
96
+ let(:subject) { Miro::DominantColors.new('http://domain.com/to/image.jpg') }
97
+
98
+ before do
99
+ subject.stub(:open_downsampled_image).and_return(mock_downsampled_image)
100
+ Tempfile.stub(:open).and_return(mock_source_image)
101
+ end
102
+
103
+ it "opens for the file resource" do
104
+ Tempfile.should_receive(:open).with(['source','.jpg']).and_return(mock_source_image)
105
+ subject.sorted_pixels
106
+ end
107
+
108
+ it "deletes the temporary source file when finished" do
109
+ mock_source_image.should_receive(:close).with(true)
110
+ subject.sorted_pixels
111
+ end
112
+ end
113
+
114
+ context "when retrieving various color values" do
115
+ before { subject.stub(:sorted_pixels).and_return(expected_pixels) }
116
+
117
+ describe "#to_hex" do
118
+ it "converts sorted pixel data into hex color values" do
119
+ subject.to_hex.should eq(expected_hex_values)
120
+ end
121
+ end
122
+
123
+ describe "#to_rgb" do
124
+ it "converts sorted pixel data into rgb color values" do
125
+ subject.to_rgb.should eq(expected_rgb_values)
126
+ end
127
+ end
128
+
129
+ describe "#to_rgba" do
130
+ it "converts sorted pixel data into rgba color values" do
131
+ subject.to_rgba.should eq(expected_rgba_values)
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ context "when find percentages of each color" do
138
+ let(:pixel_data) {
139
+ [ 2678156287,
140
+ 1362307839, 1362307839,
141
+ 2506379263, 2506379263, 2506379263, 2506379263,
142
+ 2739747583, 2739747583, 2739747583
143
+ ] }
144
+
145
+ describe "#by_percentage" do
146
+ before do
147
+ File.stub(:open).and_return(mock_source_image)
148
+ subject.stub(:open_downsampled_image).and_return(mock_downsampled_image)
149
+ end
150
+
151
+ it "returns an array of percents" do
152
+ subject.by_percentage.should == [0.4, 0.3, 0.2, 0.1]
153
+ end
154
+ end
155
+ end
156
+
157
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe Miro do
4
+ describe '.options' do
5
+ it "has default options" do
6
+ Miro.options[:image_magick_path].should eq('/usr/bin/convert')
7
+ Miro.options[:color_count].should eq(8)
8
+ Miro.options[:resolution].should eq('150x150')
9
+ end
10
+
11
+ it "can override the default options" do
12
+ Miro.options[:image_magick_path] = '/path/to/command'
13
+ Miro.options[:image_magick_path].should == '/path/to/command'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ #
8
+ require 'miro'
9
+ require 'fakeweb'
10
+
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+ end
16
+
17
+ FakeWeb.register_uri(:get, "http://domain.com/to/image.jpg", :body => "image data")
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: davinci
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jon Buda
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cocaine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: oily_png
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: fakeweb
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Extract the dominant colors from an image.
79
+ email:
80
+ - jon.buda@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .rspec
87
+ - Gemfile
88
+ - LICENSE
89
+ - README.md
90
+ - Rakefile
91
+ - lib/miro.rb
92
+ - lib/miro/dominant_colors.rb
93
+ - lib/miro/version.rb
94
+ - miro.gemspec
95
+ - spec/miro/dominant_colors_spec.rb
96
+ - spec/miro_spec.rb
97
+ - spec/spec_helper.rb
98
+ homepage: https://github.com/jonbuda/miro
99
+ licenses: []
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ requirements:
117
+ - ImageMagick
118
+ rubyforge_project:
119
+ rubygems_version: 1.8.24
120
+ signing_key:
121
+ specification_version: 3
122
+ summary: Extract the dominant colors from an image.
123
+ test_files: []
124
+ has_rdoc: