smartcropper 0.6.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.
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # croptoelie
2
+
3
+ Content aware cropping.
4
+
5
+ Crops images based on entropy: leaving the most interesting part intact.
6
+
7
+ Don't expect this to be a replacement for human cropping, it is an algorythm and not an extremely smart one at that :).
8
+
9
+ Best results achieved in combination with scaling: the cropping is then only used to square the image, cutting off the least interesting part.
10
+ The trimming simply chops off te edge that is least interesting, and continues doing so, untill it reached the requested size.
11
+
12
+ ## Usage
13
+
14
+ Use it in [carrierwave][1], in a custom `manipulate!` block. For example, carrierwave in a Rails project:
15
+
16
+ File *uploaders/attachement_uploader.rb*:
17
+
18
+ def smart_crop_and_scale(width, height)
19
+ manipulate! do |img|
20
+ img = CropToelie.new(img)
21
+ img = img.smart_crop_and_scale(width, height)
22
+ img = yield(img) if block_given?
23
+ img
24
+ end
25
+ end
26
+
27
+ # Create different versions of your uploaded files:
28
+ version :thumb do
29
+ process :smart_crop_and_scale => [80, 80]
30
+ end
31
+
32
+ ## Contributing to croptoelie
33
+
34
+ * This is one of my first more complex Ruby gems. So any help in general improvement is welcome. If you read the code and thing "OMG, what was he thinking, the answer is probably 'I wasn't'". Feel free to tell me so.
35
+ * RMagick is not the cleanest and leanest of all image-manipulation libraries in Ruby, but it was the only one where I found enough documentation and that had the features I needed (such as histograms). If you have better ideas, feel free to tell me them.
36
+ * I only use this gem with [carrierwave][1], so other implementations are probably not well done. If you want to use it in any other project, please tell me what I should change to make your life easier.
37
+ * The integration in carrierwave should be simpler. I would love to be able to say `process :smart_crop_and_scale` instead of having to use the smartcropper as class in a custom carrierwave `manipulate!` block. My knowledge of Ruby, Carrierwave and how to get this integration done properly is limited, if you have a patch, or a suggestion, that would be great!
38
+
39
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
40
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
41
+ * Fork the project
42
+ * Start a feature/bugfix branch
43
+ * Commit and push until you are happy with your contribution
44
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
45
+
46
+ ## Changelog
47
+ 2011-04-19: Replace crop with crop! avoids copying large chunks of images around.
48
+ 2011-04-18: Limit to N steps, instead of step_size.
49
+ 2011-04-16: Introduce tests and a profiler script, to profile performance.
50
+
51
+ ## Todo
52
+ Improved algorythm: first @image.scale by F, investigate the entropy on that, most-interesting square by factor F is to-be-cropped area.
53
+
54
+ ## Copyright
55
+
56
+ Copyright (c) 2011 Bèr Kessels. See LICENSE.txt for
57
+ further details.
58
+
59
+ [1]: https://github.com/jnicklas/carrierwave
60
+
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "smartcropper"
18
+ gem.homepage = "http://github.com/berkes/smartcropper"
19
+ gem.license = "GPL"
20
+ gem.summary = %Q{Content aware cropper.}
21
+ gem.description = %Q{Crops images based on entropy: leaving the most interesting part intact. Don't expect this to be a replacement for human cropping, it is an algorythm and not an extremely smart one at that :). Best results achieved in combination with scaling: the cropping is then only used to square the image, cutting off the least interesting part. The trimming simply chops off te edge that is least interesting, and continues doing so, untill it reached the requested size.}
22
+ gem.email = "ber@webschuur.com"
23
+ gem.authors = ["Bèr Kessels"]
24
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
25
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
26
+
27
+ gem.add_runtime_dependency 'rmagick', '> 2.11.0'
28
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
29
+ end
30
+ Jeweler::RubygemsDotOrgTasks.new
31
+
32
+ require 'rake/testtask'
33
+ Rake::TestTask.new(:test) do |test|
34
+ test.libs << 'lib' << 'test'
35
+ test.pattern = 'test/**/test_*.rb'
36
+ test.verbose = true
37
+ end
38
+
39
+ require 'rcov/rcovtask'
40
+ Rcov::RcovTask.new do |test|
41
+ test.libs << 'test'
42
+ test.pattern = 'test/**/test_*.rb'
43
+ test.verbose = true
44
+ end
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "smartcropper #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.6.0
@@ -0,0 +1,144 @@
1
+ require 'RMagick'
2
+ class SmartCropper
3
+ include Magick
4
+
5
+ attr_accessor :orig
6
+ attr_accessor :steps
7
+
8
+ # Create a new SmartCropper object from a ImageList single image object.
9
+ # If you want to provide a file by its path use SmartCropper.from_file('/path/to/image.png').
10
+ def initialize(image)
11
+ @image = image
12
+
13
+ # Hardcoded (but overridable) defaults.
14
+ @steps = 10
15
+
16
+ # Preprocess image.
17
+ @image = @image.quantize
18
+
19
+ # Prepare some often-used internal variables.
20
+ @rows = @image.rows
21
+ @columns = @image.columns
22
+ end
23
+
24
+ # Open create a smartcropper from a file on disk.
25
+ def self.from_file(image_path)
26
+ image = ImageList.new(image_path).last
27
+ return SmartCropper.new(image)
28
+ end
29
+
30
+ # Crops an image to width x height
31
+ def smart_crop(width, height)
32
+ sq = square(width, height)
33
+ return @image.crop!(sq[:left], sq[:top], width, height, true)
34
+ end
35
+
36
+ # Squares an image (with smart_square) and then scales that to width, heigh
37
+ def smart_crop_and_scale(width, height)
38
+ smart_square
39
+ return @image.scale!(width, height)
40
+ end
41
+
42
+ # Squares an image by slicing off the least interesting parts.
43
+ # Usefull for squaring images such as thumbnails. Usefull before scaling.
44
+ def smart_square
45
+ if @rows != @columns #None-square images must be shaved off.
46
+ if @rows < @columns #landscape
47
+ crop_height = crop_width = @rows
48
+ else # portrait
49
+ crop_height = crop_width = @columns
50
+ end
51
+
52
+ sq = square(crop_width, crop_height)
53
+ @image.crop!(sq[:left], sq[:top], crop_width, crop_height, true)
54
+ end
55
+
56
+ @image
57
+ end
58
+
59
+ # Finds the most interesting square with size width x height.
60
+ #
61
+ # Returns a hash {:left => left, :top => top, :right => right, :bottom => bottom}
62
+ def square(width, height)
63
+ return smart_crop_by_trim(width, height)
64
+ end
65
+
66
+ private
67
+ # Determines if the image should be cropped.
68
+ # Image should be cropped if original is larger then requested size.
69
+ # In all other cases, it should not.
70
+ def should_crop?
71
+ return (@columns > @width) && (@rows < @height)
72
+ end
73
+
74
+ def smart_crop_by_trim(requested_x, requested_y)
75
+ left, top = 0, 0
76
+ right, bottom = @columns, @rows
77
+ width, height = right, bottom
78
+ step_size = step_size(requested_x, requested_y)
79
+
80
+ # Avoid attempts to slice less then one pixel.
81
+ if step_size > 0
82
+ # Slice from left and right edges until the correct width is reached.
83
+ while (width > requested_x)
84
+ slice_width = [(width - requested_x), step_size].min
85
+
86
+ left_entropy = entropy_slice(@image, left, 0, slice_width, bottom)
87
+ right_entropy = entropy_slice(@image, (right - slice_width), 0, slice_width, bottom)
88
+
89
+ #remove the slice with the least entropy
90
+ if left_entropy < right_entropy
91
+ left += slice_width
92
+ else
93
+ right -= slice_width
94
+ end
95
+
96
+ width = (right - left)
97
+ end
98
+
99
+ # Slice from top and bottom edges until the correct height is reached.
100
+ while (height > requested_y)
101
+ slice_height = [(height - step_size), step_size].min
102
+
103
+ top_entropy = entropy_slice(@image, 0, top, @columns, slice_height)
104
+ bottom_entropy = entropy_slice(@image, 0, (bottom - slice_height), @columns, slice_height)
105
+
106
+ #remove the slice with the least entropy
107
+ if top_entropy < bottom_entropy
108
+ top += slice_height
109
+ else
110
+ bottom -= slice_height
111
+ end
112
+
113
+ height = (bottom - top)
114
+ end
115
+ end
116
+
117
+ square = {:left => left, :top => top, :right => right, :bottom => bottom}
118
+ end
119
+
120
+ # Compute the entropy of an image slice.
121
+ def entropy_slice(image_data, x, y, width, height)
122
+ slice = image_data.crop(x, y, width, height)
123
+ entropy = entropy(slice)
124
+ end
125
+
126
+ # Compute the entropy of an image, defined as -sum(p.*log2(p)).
127
+ # Note: instead of log2, only available in ruby > 1.9, we use
128
+ # log(p)/log(2). which has the same effect.
129
+ def entropy(image_slice)
130
+ hist = image_slice.color_histogram
131
+ hist_size = hist.values.inject{|sum,x| sum ? sum + x : x }.to_f
132
+
133
+ entropy = 0
134
+ hist.values.each do |h|
135
+ p = h.to_f / hist_size
136
+ entropy += (p * (Math.log(p)/Math.log(2))) if p != 0
137
+ end
138
+ return entropy * -1
139
+ end
140
+
141
+ def step_size(requested_x, requested_y)
142
+ ((([@rows - requested_x, @columns - requested_y].max)/2)/@steps).to_i
143
+ end
144
+ end
@@ -0,0 +1,70 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{smartcropper}
8
+ s.version = "0.6.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Bèr Kessels"]
12
+ s.date = %q{2011-04-19}
13
+ s.description = %q{Crops images based on entropy: leaving the most interesting part intact. Usefull for automatic cropping or resizing of images. }
14
+ s.email = %q{ber@webschuur.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".rvmrc",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "smartcropper.gemspec",
29
+ "lib/smartcropper.rb",
30
+ ]
31
+ s.homepage = %q{http://github.com/berkes/smartcropper}
32
+ s.licenses = ["GPL"]
33
+ s.require_paths = ["lib"]
34
+ s.rubygems_version = %q{1.5.2}
35
+ s.summary = %q{Content aware image cropping in Ruby and Carrierwave}
36
+ s.test_files = [
37
+ "test/helper.rb",
38
+ "test/profiler.rb",
39
+ "test/test_croptoelie.rb",
40
+ "test/fixtures/*"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<rmagick>, [">= 0"])
48
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
51
+ s.add_development_dependency(%q<rcov>, [">= 0"])
52
+ s.add_runtime_dependency(%q<rmagick>, ["> 2.11.0"])
53
+ else
54
+ s.add_dependency(%q<rmagick>, [">= 0"])
55
+ s.add_dependency(%q<shoulda>, [">= 0"])
56
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
57
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
58
+ s.add_dependency(%q<rcov>, [">= 0"])
59
+ s.add_dependency(%q<rmagick>, ["> 2.11.0"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<rmagick>, [">= 0"])
63
+ s.add_dependency(%q<shoulda>, [">= 0"])
64
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
66
+ s.add_dependency(%q<rcov>, [">= 0"])
67
+ s.add_dependency(%q<rmagick>, ["> 2.11.0"])
68
+ end
69
+ end
70
+
Binary file
Binary file
@@ -0,0 +1 @@
1
+ This is not an image.
Binary file
Binary file
Binary file
data/test/helper.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+ require 'test/unit'
12
+ require 'shoulda'
13
+
14
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
15
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
16
+ require 'smartcropper'
17
+
data/test/profiler.rb ADDED
@@ -0,0 +1,37 @@
1
+ require 'ruby-prof'
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ require 'smartcropper'
5
+
6
+ tests = {
7
+ :smart_crop_by_trim => {:method => :smart_crop, :params => [100, 100]},
8
+ # :smart_crop_by_search => {:method => :smart_crop, :params => [100, 100, :by_search]},
9
+ :smart_crop_and_scale => {:method => :smart_crop_and_scale, :params => [100, 100]},
10
+ :smart_square => {:method => :smart_square, :params => []}
11
+ }
12
+ #result = RubyProf.profile do
13
+ tests.each do |id, test|
14
+ filename = File.join(File.expand_path(File.dirname(__FILE__)), "../doc/tyto.jpg")
15
+
16
+ 2.times do |i|
17
+ # result = RubyProf.profile do
18
+ img = SmartCropper.from_file(filename)
19
+ img.send(test[:method], *test[:params])
20
+ img = nil
21
+ # end
22
+
23
+ # # Print a flat profile to text
24
+ # puts "Run #{i}:\t #{id} ------------------------"
25
+ # #file = File.new("./#{id}-#{i}.txt", "w")
26
+ # printer = RubyProf::FlatPrinter.new(result)
27
+ # printer.print(STDOUT, {:min_percent => 10})
28
+ # print = nil
29
+ end
30
+ end
31
+ #end
32
+ # # Print a flat profile to text
33
+ # puts "Run #{i}:\t #{id} ------------------------"
34
+ # #file = File.new("./#{id}-#{i}.txt", "w")
35
+ # printer = RubyProf::FlatPrinter.new(result)
36
+ # printer.print(STDOUT, {:min_percent => 10})
37
+ # print = nil
@@ -0,0 +1,79 @@
1
+ require 'helper'
2
+
3
+ class TestSmartcropper < Test::Unit::TestCase
4
+ def setup
5
+ @filename = File.join(File.expand_path(File.dirname(__FILE__)), "fixtures", "entropyish.png")
6
+ @image = Magick::ImageList.new(@filename).last
7
+
8
+ @twenty_twenty = Magick::ImageList.new(
9
+ File.join(File.expand_path(File.dirname(__FILE__)), "fixtures", "20x20.png")
10
+ ).last
11
+ end
12
+
13
+ should "initialize a smartcropper image from an ImageList item" do
14
+ img = SmartCropper.new(@image)
15
+ assert_equal(img.class, SmartCropper)
16
+ end
17
+ should "create a smartcropper from an imagefile" do
18
+ img = SmartCropper.from_file(@filename)
19
+ assert_equal(img.class, SmartCropper)
20
+ end
21
+
22
+ should "fail on creating a smartcropper image from a textfile" do
23
+ assert_raise Magick::ImageMagickError, NoMethodError do
24
+ SmartCropper.new(File.join(File.expand_path(File.dirname(__FILE__)), "fixtures","entropyish.txt"))
25
+ end
26
+ end
27
+
28
+ should "crop to 100x100 without scaling with smart_crop" do
29
+ img = SmartCropper.new(@image)
30
+ img = img.smart_crop(100, 100)
31
+ size = [img.rows, img.columns]
32
+ assert_equal(size, [100, 100])
33
+ end
34
+
35
+ should "crop to 100x100 with scaling with smart_crop_and_scale" do
36
+ img = SmartCropper.new(@image)
37
+ img = img.smart_crop_and_scale(100, 100)
38
+ size = [img.rows, img.columns]
39
+ assert_equal(size, [100, 100])
40
+ end
41
+
42
+ should "square image without scaling" do
43
+ img = SmartCropper.new(@image)
44
+ img = img.smart_square
45
+ assert_equal(img.rows, img.columns)
46
+ end
47
+
48
+ should "not crop small images" do
49
+ img = SmartCropper.new(@twenty_twenty)
50
+ img = img.smart_crop(100, 100)
51
+ size = [img.rows, img.columns]
52
+ assert_equal([20, 20], size)
53
+ end
54
+
55
+ should "still crop a slice of one pixel" do
56
+ img = SmartCropper.new(@twenty_twenty)
57
+ img = img.smart_crop(19, 19)
58
+ size = [img.rows, img.columns]
59
+ assert_equal([19, 19], size)
60
+ end
61
+
62
+ ###########################################################################
63
+ # Images reported to fail by issue #5 #
64
+ ###########################################################################
65
+ [:smart_crop, :smart_crop_and_scale, :smart_square].each do |method|
66
+ full_path = File.join File.dirname(__FILE__), "fixtures", "errors"
67
+ Dir.open(full_path).select{|f| !File.directory?(f)}.each do |file|
68
+
69
+ should "'not fail on reported-as-broken image '#{file}' with '#{method}'" do
70
+ realpath = File.realpath(File.join full_path, file)
71
+
72
+ img = SmartCropper.new(Magick::ImageList.new(realpath).last)
73
+ img = img.smart_crop(200, 200)
74
+ size = [img.rows, img.columns]
75
+ assert_equal(size, [200, 200])
76
+ end
77
+ end
78
+ end
79
+ end
metadata ADDED
@@ -0,0 +1,173 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: smartcropper
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bèr Kessels
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rmagick
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: shoulda
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
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: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.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: 1.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: jeweler
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 1.5.2
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: 1.5.2
78
+ - !ruby/object:Gem::Dependency
79
+ name: rcov
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rmagick
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>'
100
+ - !ruby/object:Gem::Version
101
+ version: 2.11.0
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>'
108
+ - !ruby/object:Gem::Version
109
+ version: 2.11.0
110
+ description: ! 'Crops images based on entropy: leaving the most interesting part intact.
111
+ Don''t expect this to be a replacement for human cropping, it is an algorythm and
112
+ not an extremely smart one at that :). Best results achieved in combination with
113
+ scaling: the cropping is then only used to square the image, cutting off the least
114
+ interesting part. The trimming simply chops off te edge that is least interesting,
115
+ and continues doing so, untill it reached the requested size.'
116
+ email: ber@webschuur.com
117
+ executables: []
118
+ extensions: []
119
+ extra_rdoc_files:
120
+ - LICENSE.txt
121
+ - README.md
122
+ files:
123
+ - .document
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - LICENSE.txt
127
+ - README.md
128
+ - Rakefile
129
+ - VERSION
130
+ - lib/smartcropper.rb
131
+ - smartcropper.gemspec
132
+ - test/fixtures/20x20.png
133
+ - test/fixtures/entropyish.png
134
+ - test/fixtures/entropyish.txt
135
+ - test/fixtures/errors/flo-rida.png
136
+ - test/fixtures/errors/hollywood-undead.jpg
137
+ - test/fixtures/errors/maroon-5.jpg
138
+ - test/fixtures/errors/yo-gotti.jpg
139
+ - test/helper.rb
140
+ - test/profiler.rb
141
+ - test/test_croptoelie.rb
142
+ homepage: http://github.com/berkes/smartcropper
143
+ licenses:
144
+ - GPL
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ segments:
156
+ - 0
157
+ hash: 949373039
158
+ required_rubygems_version: !ruby/object:Gem::Requirement
159
+ none: false
160
+ requirements:
161
+ - - ! '>='
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubyforge_project:
166
+ rubygems_version: 1.8.24
167
+ signing_key:
168
+ specification_version: 3
169
+ summary: Content aware cropper.
170
+ test_files:
171
+ - test/helper.rb
172
+ - test/profiler.rb
173
+ - test/test_croptoelie.rb