davinci 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: