paperclip-facecrop 0.0.2 → 0.0.3
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/lib/paperclip-facecrop.rb +106 -116
- data/paperclip-facecrop.gemspec +3 -3
- metadata +4 -5
- data/lib/version.rb +0 -5
data/lib/paperclip-facecrop.rb
CHANGED
@@ -1,158 +1,148 @@
|
|
1
|
-
require '
|
1
|
+
require 'opencv_ext'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
"#{self.x},#{self.y}-#{self.width}x#{self.height}"
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
class Paperclip::FaceCrop < Paperclip::Thumbnail
|
3
|
+
module Paperclip
|
4
|
+
class FaceCrop < Paperclip::Thumbnail
|
10
5
|
|
11
|
-
|
6
|
+
@@debug = false
|
12
7
|
|
13
|
-
|
14
|
-
|
8
|
+
cattr_accessor :classifiers
|
9
|
+
cattr_accessor :debug
|
15
10
|
|
16
|
-
|
17
|
-
|
11
|
+
def initialize(file, options = {}, attachment = nil)
|
12
|
+
super(file, options, attachment)
|
18
13
|
|
19
|
-
|
20
|
-
|
14
|
+
faces_regions = []
|
15
|
+
faces_parts_regions = []
|
21
16
|
|
22
|
-
|
17
|
+
raise "No classifiers were defined" if self.classifiers.nil?
|
23
18
|
|
24
|
-
|
19
|
+
image = OpenCV::IplImage.load(file.path, 1)
|
25
20
|
|
26
|
-
|
21
|
+
faces_regions = detect_regions(image, self.classifiers[:face])
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
#Paperclip::FaceCrop.classifiers[:nose]
|
24
|
+
unless self.classifiers[:parts].nil?
|
25
|
+
faces_parts_regions = detect_regions(image, self.classifiers[:parts], OpenCV::CvColor::Red)
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
27
|
+
faces_regions.reject! do |face_region|
|
28
|
+
region = faces_parts_regions.detect do |part_region|
|
29
|
+
face_region.collide?(part_region)
|
30
|
+
end
|
36
31
|
|
37
|
-
|
32
|
+
region.nil?
|
33
|
+
end
|
38
34
|
end
|
39
|
-
end
|
40
35
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
x_coords = []
|
37
|
+
y_coords = []
|
38
|
+
widths = []
|
39
|
+
heights = []
|
45
40
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
41
|
+
faces_regions.each do |region|
|
42
|
+
x_coords << region.top_left.x << region.bottom_right.x
|
43
|
+
y_coords << region.top_left.y << region.bottom_right.y
|
44
|
+
widths << region.width
|
45
|
+
heights << region.height
|
46
|
+
end
|
52
47
|
|
53
|
-
|
48
|
+
@has_faces = faces_regions.size > 0
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
50
|
+
if @has_faces
|
51
|
+
@top_left_x = x_coords.min
|
52
|
+
@top_left_y = y_coords.min
|
53
|
+
@bottom_right_x = x_coords.max
|
54
|
+
@bottom_right_y = y_coords.max
|
60
55
|
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
# average faces areas
|
57
|
+
average_face_width = widths.sum / widths.size
|
58
|
+
average_face_height = heights.sum / heights.size
|
64
59
|
|
65
|
-
|
66
|
-
|
60
|
+
# calculating the surrounding margin of the area that covers all the found faces
|
61
|
+
#
|
67
62
|
|
68
|
-
|
69
|
-
|
70
|
-
|
63
|
+
# new width
|
64
|
+
@top_left_x -= average_face_width / 2
|
65
|
+
@bottom_right_x += average_face_width / 2
|
71
66
|
|
72
|
-
|
73
|
-
|
74
|
-
|
67
|
+
# new height
|
68
|
+
@top_left_y -= average_face_height / 2
|
69
|
+
@bottom_right_y += average_face_height / 1.6
|
75
70
|
|
76
|
-
calculate_bounds
|
77
|
-
|
78
|
-
# if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled
|
79
|
-
#
|
80
|
-
if @faces_width < @target_geometry.width
|
81
|
-
delta_width = (@target_geometry.width - @faces_width) / 2
|
82
|
-
@top_left_x -= delta_width
|
83
|
-
@bottom_right_x += delta_width
|
84
71
|
calculate_bounds
|
85
|
-
end
|
86
72
|
|
87
|
-
|
73
|
+
# if the new area is smaller than the target geometry, it's scaled so the final image isn't resampled
|
74
|
+
#
|
75
|
+
if @faces_width < @target_geometry.width
|
76
|
+
delta_width = (@target_geometry.width - @faces_width) / 2
|
77
|
+
@top_left_x -= delta_width
|
78
|
+
@bottom_right_x += delta_width
|
79
|
+
calculate_bounds
|
80
|
+
end
|
88
81
|
|
89
|
-
|
90
|
-
delta_height = (@target_geometry.height - @faces_height) / 2
|
91
|
-
@top_left_y -= delta_height
|
92
|
-
@bottom_right_y += delta_height
|
93
|
-
calculate_bounds
|
94
|
-
end
|
82
|
+
#raise (@target_geometry.height > 0 and @faces_height < @target_geometry.height).to_s
|
95
83
|
|
96
|
-
|
84
|
+
if (@target_geometry.height > 0 and @faces_height < @target_geometry.height)
|
85
|
+
delta_height = (@target_geometry.height - @faces_height) / 2
|
86
|
+
@top_left_y -= delta_height
|
87
|
+
@bottom_right_y += delta_height
|
88
|
+
calculate_bounds
|
89
|
+
end
|
97
90
|
|
98
|
-
|
99
|
-
|
91
|
+
@faces_height = @faces_width if @target_geometry.height == 0
|
92
|
+
|
93
|
+
@current_geometry = Paperclip::Geometry.new(@faces_width, @faces_height)
|
94
|
+
end
|
100
95
|
|
101
|
-
|
96
|
+
end
|
102
97
|
|
103
98
|
|
104
|
-
|
105
|
-
|
99
|
+
def transformation_command
|
100
|
+
return super unless @has_faces
|
106
101
|
|
107
|
-
|
108
|
-
|
102
|
+
scale, crop = @current_geometry.transformation_to(@target_geometry, crop?)
|
103
|
+
faces_crop = "%dx%d+%d+%d" % [@faces_width, @faces_height, @top_left_x, @top_left_y]
|
109
104
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
105
|
+
trans = []
|
106
|
+
trans << "-crop" << %["#{faces_crop}"] << "+repage"
|
107
|
+
trans << "-resize" << %["#{scale}"] unless scale.nil? || scale.empty?
|
108
|
+
trans << "-crop" << %["#{crop}"] << "+repage" if crop
|
109
|
+
trans
|
110
|
+
end
|
116
111
|
|
117
|
-
|
112
|
+
private
|
118
113
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
114
|
+
# calculate_bounds
|
115
|
+
#
|
116
|
+
def calculate_bounds
|
117
|
+
@top_left_x = 0 if @top_left_x < 0
|
118
|
+
@bottom_right_x = @current_geometry.width if @bottom_right_x > @current_geometry.width
|
124
119
|
|
125
|
-
|
126
|
-
|
120
|
+
@top_left_y = 0 if @top_left_y < 0
|
121
|
+
@bottom_right_y = @current_geometry.height if @bottom_right_y > @current_geometry.height
|
127
122
|
|
128
|
-
|
129
|
-
|
130
|
-
|
123
|
+
@faces_width = @bottom_right_x - @top_left_x
|
124
|
+
@faces_height = @bottom_right_y - @top_left_y
|
125
|
+
end
|
131
126
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
127
|
+
# detect_regions
|
128
|
+
#
|
129
|
+
def detect_regions(image, classifiers, color = OpenCV::CvColor::Blue)
|
130
|
+
regions = []
|
136
131
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
132
|
+
classifiers.each do |classifier|
|
133
|
+
detector = OpenCV::CvHaarClassifierCascade::load(classifier)
|
134
|
+
detector.detect_objects(image) do |region|
|
135
|
+
regions << region
|
136
|
+
image.rectangle!(region.top_left, region.bottom_right, :color => color) if self.debug
|
137
|
+
end
|
142
138
|
end
|
143
|
-
end
|
144
139
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
140
|
+
if self.debug
|
141
|
+
image.save_image(@file.path)
|
142
|
+
Rails.logger.info(regions)
|
143
|
+
end
|
149
144
|
|
150
|
-
|
151
|
-
|
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);
|
145
|
+
regions
|
146
|
+
end
|
157
147
|
end
|
158
148
|
end
|
data/paperclip-facecrop.gemspec
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
|
3
|
+
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "paperclip-facecrop"
|
6
|
-
s.version =
|
6
|
+
s.version = "0.0.3"
|
7
7
|
s.authors = ["Borja Martín"]
|
8
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
9
|
s.summary = %q{Paperclip processor that is aware of the faces found on the image}
|
@@ -13,5 +13,5 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.has_rdoc = false
|
15
15
|
s.add_runtime_dependency("paperclip")
|
16
|
-
s.add_runtime_dependency("opencv", "
|
16
|
+
s.add_runtime_dependency("opencv", "= 0.0.6")
|
17
17
|
end
|
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
|
+
- 3
|
9
|
+
version: 0.0.3
|
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-10 00:00:00 +02:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -36,7 +36,7 @@ dependencies:
|
|
36
36
|
requirement: &id002 !ruby/object:Gem::Requirement
|
37
37
|
none: false
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - "="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
segments:
|
42
42
|
- 0
|
@@ -61,7 +61,6 @@ files:
|
|
61
61
|
- README_example_b.jpg
|
62
62
|
- Rakefile
|
63
63
|
- lib/paperclip-facecrop.rb
|
64
|
-
- lib/version.rb
|
65
64
|
- paperclip-facecrop.gemspec
|
66
65
|
has_rdoc: true
|
67
66
|
homepage: http://github.com/dagi3d/paperclip-facecrop
|