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/.document +5 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +674 -0
- data/README.md +60 -0
- data/Rakefile +56 -0
- data/VERSION +1 -0
- data/lib/smartcropper.rb +144 -0
- data/smartcropper.gemspec +70 -0
- data/test/fixtures/20x20.png +0 -0
- data/test/fixtures/entropyish.png +0 -0
- data/test/fixtures/entropyish.txt +1 -0
- data/test/fixtures/errors/flo-rida.png +0 -0
- data/test/fixtures/errors/hollywood-undead.jpg +0 -0
- data/test/fixtures/errors/maroon-5.jpg +0 -0
- data/test/fixtures/errors/yo-gotti.jpg +0 -0
- data/test/helper.rb +17 -0
- data/test/profiler.rb +37 -0
- data/test/test_croptoelie.rb +79 -0
- metadata +173 -0
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
|
data/lib/smartcropper.rb
ADDED
@@ -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
|
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
|