paperclip-facecrop 0.0.1 → 0.0.2
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/Gemfile +0 -0
- data/README.md +42 -7
- data/README_example_b.jpg +0 -0
- data/lib/paperclip-facecrop.rb +86 -29
- data/lib/version.rb +1 -1
- metadata +4 -3
data/Gemfile
CHANGED
Binary file
|
data/README.md
CHANGED
@@ -13,19 +13,47 @@ Requirements:
|
|
13
13
|
|
14
14
|
Installation:
|
15
15
|
-------------
|
16
|
-
-
|
17
|
-
|
18
|
-
`gem install paperclip-facecrop'`
|
19
|
-
|
20
|
-
- Add to `Gemfile`
|
16
|
+
- Add to your application `Gemfile`
|
21
17
|
|
22
18
|
gem 'paperclip-facecrop'
|
19
|
+
|
20
|
+
- Type
|
21
|
+
|
22
|
+
bundle install
|
23
23
|
|
24
24
|
- Write an initializer setting the path of the haarcascade filters(`initializers/paperclip.rb` for example):
|
25
25
|
|
26
|
-
Paperclip::FaceCrop.classifiers =
|
26
|
+
Paperclip::FaceCrop.classifiers = {
|
27
|
+
:face => ["/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml"]
|
28
|
+
}
|
29
|
+
|
30
|
+
You can use more than one filter to try more accurate searches:
|
31
|
+
|
32
|
+
Paperclip::FaceCrop.classifiers = {
|
33
|
+
:face => [
|
34
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml",
|
35
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml",
|
36
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_profileface.xml"
|
37
|
+
]
|
38
|
+
}
|
39
|
+
|
40
|
+
In order to try to avoid some false positives, you can also specify other classifiers to detect other parts of the face. In that case,
|
41
|
+
only the found areas that contain parts like a mouth, an eye or a nose will be considered a face:
|
42
|
+
|
43
|
+
Paperclip::FaceCrop.classifiers = {
|
44
|
+
:face => [
|
45
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt.xml",
|
46
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt_tree.xml",
|
47
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_profileface.xml"
|
48
|
+
],
|
49
|
+
:parts => [
|
50
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_mcs_nose.xml",
|
51
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_mcs_lefteye.xml",
|
52
|
+
"/usr/local/share/opencv/haarcascades/haarcascade_mcs_righteye.xml"
|
53
|
+
]
|
54
|
+
}
|
55
|
+
|
27
56
|
|
28
|
-
You can use more than one filter if you want in order to try more accurate searches.
|
29
57
|
|
30
58
|
Usage:
|
31
59
|
------
|
@@ -40,6 +68,13 @@ Just specify your image styles as usual and set :face_crop as the processor:
|
|
40
68
|
|
41
69
|
In case no faces were found, it will behave simply as the `Paperclip::Thumbnail` processor
|
42
70
|
|
71
|
+
|
72
|
+
You can also set the debug mode to draw on the image the detected regions:
|
73
|
+
|
74
|
+
Paperclip::FaceCrop.debug = (Rails.env == 'development')
|
75
|
+
|
76
|
+

|
77
|
+
|
43
78
|
Credits:
|
44
79
|
--------
|
45
80
|
Copyright (c) 2011 Borja Martín Sánchez de Vivar <borjamREMOVETHIS@dagi3d.net> - <http://dagi3d.net>, released under the MIT license
|
Binary file
|
data/lib/paperclip-facecrop.rb
CHANGED
@@ -1,33 +1,57 @@
|
|
1
1
|
require 'opencv'
|
2
2
|
|
3
|
+
class OpenCV::CvAvgComp
|
4
|
+
def to_s
|
5
|
+
"#{self.x},#{self.y}-#{self.width}x#{self.height}"
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
3
9
|
class Paperclip::FaceCrop < Paperclip::Thumbnail
|
10
|
+
|
11
|
+
@@debug = false
|
4
12
|
|
5
13
|
cattr_accessor :classifiers
|
6
|
-
|
14
|
+
cattr_accessor :debug
|
15
|
+
|
7
16
|
def initialize(file, options = {}, attachment = nil)
|
8
17
|
super(file, options, attachment)
|
9
18
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
19
|
+
faces_regions = []
|
20
|
+
faces_parts_regions = []
|
21
|
+
|
22
|
+
raise "No classifiers were defined" if self.classifiers.nil?
|
14
23
|
|
15
|
-
|
24
|
+
image = OpenCV::IplImage.load(file.path, 1)
|
16
25
|
|
17
|
-
|
26
|
+
faces_regions = detect_regions(image, self.classifiers[:face])
|
18
27
|
|
19
|
-
Paperclip::FaceCrop.classifiers
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
#Paperclip::FaceCrop.classifiers[:nose]
|
29
|
+
unless self.classifiers[:parts].nil?
|
30
|
+
faces_parts_regions = detect_regions(image, self.classifiers[:parts], OpenCV::CvColor::Red)
|
31
|
+
|
32
|
+
faces_regions.reject! do |face_region|
|
33
|
+
region = faces_parts_regions.detect do |part_region|
|
34
|
+
regions_overlap?(face_region, part_region)
|
35
|
+
end
|
36
|
+
|
37
|
+
region.nil?
|
26
38
|
end
|
27
39
|
end
|
28
40
|
|
29
|
-
|
41
|
+
x_coords = []
|
42
|
+
y_coords = []
|
43
|
+
widths = []
|
44
|
+
heights = []
|
45
|
+
|
46
|
+
faces_regions.each do |region|
|
47
|
+
x_coords << region.top_left.x << region.bottom_right.x
|
48
|
+
y_coords << region.top_left.y << region.bottom_right.y
|
49
|
+
widths << region.width
|
50
|
+
heights << region.height
|
51
|
+
end
|
30
52
|
|
53
|
+
@has_faces = faces_regions.size > 0
|
54
|
+
|
31
55
|
if @has_faces
|
32
56
|
@top_left_x = x_coords.min
|
33
57
|
@top_left_y = y_coords.min
|
@@ -37,7 +61,7 @@ class Paperclip::FaceCrop < Paperclip::Thumbnail
|
|
37
61
|
# average faces areas
|
38
62
|
average_face_width = widths.sum / widths.size
|
39
63
|
average_face_height = heights.sum / heights.size
|
40
|
-
|
64
|
+
|
41
65
|
# calculating the surrounding margin of the area that covers all the found faces
|
42
66
|
#
|
43
67
|
|
@@ -53,25 +77,45 @@ class Paperclip::FaceCrop < Paperclip::Thumbnail
|
|
53
77
|
|
54
78
|
# if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled
|
55
79
|
#
|
56
|
-
if @faces_width < @target_geometry.width
|
80
|
+
if @faces_width < @target_geometry.width
|
57
81
|
delta_width = (@target_geometry.width - @faces_width) / 2
|
58
82
|
@top_left_x -= delta_width
|
59
83
|
@bottom_right_x += delta_width
|
60
84
|
calculate_bounds
|
61
85
|
end
|
62
86
|
|
63
|
-
|
87
|
+
#raise (@target_geometry.height > 0 and @faces_height < @target_geometry.height).to_s
|
88
|
+
|
89
|
+
if (@target_geometry.height > 0 and @faces_height < @target_geometry.height)
|
64
90
|
delta_height = (@target_geometry.height - @faces_height) / 2
|
65
91
|
@top_left_y -= delta_height
|
66
92
|
@bottom_right_y += delta_height
|
67
93
|
calculate_bounds
|
68
94
|
end
|
69
95
|
|
96
|
+
@faces_height = @faces_width if @target_geometry.height == 0
|
97
|
+
|
70
98
|
@current_geometry = Paperclip::Geometry.new(@faces_width, @faces_height)
|
71
99
|
end
|
72
100
|
|
73
101
|
end
|
74
|
-
|
102
|
+
|
103
|
+
|
104
|
+
def transformation_command
|
105
|
+
return super unless @has_faces
|
106
|
+
|
107
|
+
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
|
108
|
+
faces_crop = "%dx%d+%d+%d" % [@faces_width, @faces_height, @top_left_x, @top_left_y]
|
109
|
+
|
110
|
+
trans = []
|
111
|
+
trans << "-crop" << %["#{faces_crop}"] << "+repage"
|
112
|
+
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
|
113
|
+
trans << "-crop" << %["#{crop}"] << "+repage" if crop
|
114
|
+
trans
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
75
119
|
# calculate_bounds
|
76
120
|
#
|
77
121
|
def calculate_bounds
|
@@ -85,17 +129,30 @@ class Paperclip::FaceCrop < Paperclip::Thumbnail
|
|
85
129
|
@faces_height = @bottom_right_y - @top_left_y
|
86
130
|
end
|
87
131
|
|
88
|
-
|
89
|
-
|
90
|
-
|
132
|
+
# detect_regions
|
133
|
+
#
|
134
|
+
def detect_regions(image, classifiers, color = OpenCV::CvColor::Blue)
|
135
|
+
regions = []
|
91
136
|
|
92
|
-
|
93
|
-
|
137
|
+
classifiers.each do |classifier|
|
138
|
+
detector = OpenCV::CvHaarClassifierCascade::load(classifier)
|
139
|
+
detector.detect_objects(image) do |region|
|
140
|
+
regions << region
|
141
|
+
image.rectangle!(region.top_left, region.bottom_right, :color => color) if self.debug
|
142
|
+
end
|
143
|
+
end
|
94
144
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
145
|
+
if self.debug
|
146
|
+
image.save_image(@file.path)
|
147
|
+
Rails.logger.info(regions)
|
148
|
+
end
|
149
|
+
|
150
|
+
regions
|
151
|
+
end
|
152
|
+
|
153
|
+
def regions_overlap?(r1, r2)
|
154
|
+
dx = (r2.x - r1.x).abs + r2.width
|
155
|
+
dy = (r2.y - r1.y).abs + r2.height;
|
156
|
+
return (dx < r1.width + r2.width && dy < r1.height + r2.height);
|
100
157
|
end
|
101
158
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- "Borja Mart\xC3\xADn"
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-05-
|
17
|
+
date: 2011-05-06 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -58,6 +58,7 @@ files:
|
|
58
58
|
- Gemfile
|
59
59
|
- README.md
|
60
60
|
- README_example.jpg
|
61
|
+
- README_example_b.jpg
|
61
62
|
- Rakefile
|
62
63
|
- lib/paperclip-facecrop.rb
|
63
64
|
- lib/version.rb
|