paperclip-facecrop 0.0.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.
- data/.gitignore +4 -0
- data/Gemfile +0 -0
- data/README.md +50 -0
- data/README_example.jpg +0 -0
- data/Rakefile +2 -0
- data/lib/paperclip-facecrop.rb +101 -0
- data/lib/version.rb +5 -0
- data/paperclip-facecrop.gemspec +17 -0
- metadata +98 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
Binary file
|
data/README.md
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
Paperclip::FaceCrop
|
2
|
+
====================
|
3
|
+
`Paperclip::FaceCrop` is a [Paperclip][paperclip] processor that is aware of the faces found on the image
|
4
|
+
so that they aren't cropped or aren't shown too small while generating the thumbnails.
|
5
|
+
It uses OpenCV for the facial recognition
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
Requirements:
|
10
|
+
-------------
|
11
|
+
- [OpenCV][opencv]
|
12
|
+
- [OpenCV ruby binding][ruby-opencv]
|
13
|
+
|
14
|
+
Installation:
|
15
|
+
-------------
|
16
|
+
- Install the gem
|
17
|
+
|
18
|
+
`gem install paperclip-facecrop'`
|
19
|
+
|
20
|
+
- Add to `Gemfile`
|
21
|
+
|
22
|
+
gem 'paperclip-facecrop'
|
23
|
+
|
24
|
+
- Write an initializer setting the path of the haarcascade filters(`initializers/paperclip.rb` for example):
|
25
|
+
|
26
|
+
Paperclip::FaceCrop.classifiers = ["/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml"]
|
27
|
+
|
28
|
+
You can use more than one filter if you want in order to try more accurate searches.
|
29
|
+
|
30
|
+
Usage:
|
31
|
+
------
|
32
|
+
Just specify your image styles as usual and set :face_crop as the processor:
|
33
|
+
|
34
|
+
class Image < ActiveRecord::Base
|
35
|
+
|
36
|
+
has_attached_file :attachment,
|
37
|
+
:styles => {:thumbnail => "200x125#"},
|
38
|
+
:processors => [:face_crop]
|
39
|
+
end
|
40
|
+
|
41
|
+
In case no faces were found, it will behave simply as the `Paperclip::Thumbnail` processor
|
42
|
+
|
43
|
+
Credits:
|
44
|
+
--------
|
45
|
+
Copyright (c) 2011 Borja Martín Sánchez de Vivar <borjamREMOVETHIS@dagi3d.net> - <http://dagi3d.net>, released under the MIT license
|
46
|
+
The photo used as example belongs to [Jesper Rønn-Jensen](http://www.flickr.com/photos/jesper/)
|
47
|
+
|
48
|
+
[paperclip]: https://github.com/thoughtbot/paperclip
|
49
|
+
[opencv]: http://opencv.willowgarage.com/
|
50
|
+
[ruby-opencv]: https://github.com/ser1zw/ruby-opencv
|
data/README_example.jpg
ADDED
Binary file
|
data/Rakefile
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'opencv'
|
2
|
+
|
3
|
+
class Paperclip::FaceCrop < Paperclip::Thumbnail
|
4
|
+
|
5
|
+
cattr_accessor :classifiers
|
6
|
+
|
7
|
+
def initialize(file, options = {}, attachment = nil)
|
8
|
+
super(file, options, attachment)
|
9
|
+
|
10
|
+
x_coords = []
|
11
|
+
y_coords = []
|
12
|
+
widths = []
|
13
|
+
heights = []
|
14
|
+
|
15
|
+
raise "No classifiers were defined" if Paperclip::FaceCrop.classifiers.nil?
|
16
|
+
|
17
|
+
image = OpenCV::IplImage.load(file.path)
|
18
|
+
|
19
|
+
Paperclip::FaceCrop.classifiers.each do |classifier|
|
20
|
+
detector = OpenCV::CvHaarClassifierCascade::load(classifier)
|
21
|
+
detector.detect_objects(image) do |region|
|
22
|
+
x_coords << region.top_left.x << region.bottom_right.x
|
23
|
+
y_coords << region.top_left.y << region.bottom_right.y
|
24
|
+
widths << region.width
|
25
|
+
heights << region.height
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
@has_faces = x_coords.size > 0
|
30
|
+
|
31
|
+
if @has_faces
|
32
|
+
@top_left_x = x_coords.min
|
33
|
+
@top_left_y = y_coords.min
|
34
|
+
@bottom_right_x = x_coords.max
|
35
|
+
@bottom_right_y = y_coords.max
|
36
|
+
|
37
|
+
# average faces areas
|
38
|
+
average_face_width = widths.sum / widths.size
|
39
|
+
average_face_height = heights.sum / heights.size
|
40
|
+
|
41
|
+
# calculating the surrounding margin of the area that covers all the found faces
|
42
|
+
#
|
43
|
+
|
44
|
+
# new width
|
45
|
+
@top_left_x -= average_face_width / 2
|
46
|
+
@bottom_right_x += average_face_width / 2
|
47
|
+
|
48
|
+
# new height
|
49
|
+
@top_left_y -= average_face_height / 2
|
50
|
+
@bottom_right_y += average_face_height / 1.6
|
51
|
+
|
52
|
+
calculate_bounds
|
53
|
+
|
54
|
+
# if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled
|
55
|
+
#
|
56
|
+
if @faces_width < @target_geometry.width
|
57
|
+
delta_width = (@target_geometry.width - @faces_width) / 2
|
58
|
+
@top_left_x -= delta_width
|
59
|
+
@bottom_right_x += delta_width
|
60
|
+
calculate_bounds
|
61
|
+
end
|
62
|
+
|
63
|
+
if @faces_height < @target_geometry.height
|
64
|
+
delta_height = (@target_geometry.height - @faces_height) / 2
|
65
|
+
@top_left_y -= delta_height
|
66
|
+
@bottom_right_y += delta_height
|
67
|
+
calculate_bounds
|
68
|
+
end
|
69
|
+
|
70
|
+
@current_geometry = Paperclip::Geometry.new(@faces_width, @faces_height)
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
# calculate_bounds
|
76
|
+
#
|
77
|
+
def calculate_bounds
|
78
|
+
@top_left_x = 0 if @top_left_x < 0
|
79
|
+
@bottom_right_x = @current_geometry.width if @bottom_right_x > @current_geometry.width
|
80
|
+
|
81
|
+
@top_left_y = 0 if @top_left_y < 0
|
82
|
+
@bottom_right_y = @current_geometry.height if @bottom_right_y > @current_geometry.height
|
83
|
+
|
84
|
+
@faces_width = @bottom_right_x - @top_left_x
|
85
|
+
@faces_height = @bottom_right_y - @top_left_y
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def transformation_command
|
90
|
+
return super unless @has_faces
|
91
|
+
|
92
|
+
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
|
93
|
+
faces_crop = "%dx%d+%d+%d" % [@faces_width, @faces_height, @top_left_x, @top_left_y]
|
94
|
+
|
95
|
+
trans = []
|
96
|
+
trans << "-crop" << %["#{faces_crop}"] << "+repage"
|
97
|
+
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
|
98
|
+
trans << "-crop" << %["#{crop}"] << "+repage" if crop
|
99
|
+
trans
|
100
|
+
end
|
101
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require 'version'
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "paperclip-facecrop"
|
6
|
+
s.version = Paperclip::FaceCrop::VERSION
|
7
|
+
s.authors = ["Borja Martín"]
|
8
|
+
s.description = %q{Paperclip processor that is aware of the faces detected on the image so that they don't get cropped or aren't shown too small while generating the thumbnails}
|
9
|
+
s.summary = %q{Paperclip processor that is aware of the faces found on the image}
|
10
|
+
s.email = "borjam@dagi3d.net"
|
11
|
+
s.homepage = "http://github.com/dagi3d/paperclip-facecrop"
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.has_rdoc = false
|
15
|
+
s.add_runtime_dependency("paperclip")
|
16
|
+
s.add_runtime_dependency("opencv", "~> 0.0.6")
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paperclip-facecrop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- "Borja Mart\xC3\xADn"
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-05-03 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: paperclip
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: opencv
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
- 0
|
44
|
+
- 6
|
45
|
+
version: 0.0.6
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Paperclip processor that is aware of the faces detected on the image so that they don't get cropped or aren't shown too small while generating the thumbnails
|
49
|
+
email: borjam@dagi3d.net
|
50
|
+
executables: []
|
51
|
+
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
extra_rdoc_files: []
|
55
|
+
|
56
|
+
files:
|
57
|
+
- .gitignore
|
58
|
+
- Gemfile
|
59
|
+
- README.md
|
60
|
+
- README_example.jpg
|
61
|
+
- Rakefile
|
62
|
+
- lib/paperclip-facecrop.rb
|
63
|
+
- lib/version.rb
|
64
|
+
- paperclip-facecrop.gemspec
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/dagi3d/paperclip-facecrop
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 0
|
81
|
+
version: "0"
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
requirements: []
|
91
|
+
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 1.3.7
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: Paperclip processor that is aware of the faces found on the image
|
97
|
+
test_files: []
|
98
|
+
|