yearbook 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/go.rb +4 -1
- data/lib/yearbook/att_hash.rb +15 -0
- data/lib/yearbook/classifier.rb +26 -0
- data/lib/yearbook/image.rb +66 -32
- data/spec/spec_helper.rb +4 -0
- data/spec/yearbook_spec.rb +57 -6
- data/yearbook.gemspec +4 -2
- metadata +5 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/go.rb
CHANGED
@@ -4,7 +4,10 @@ require 'yearbook'
|
|
4
4
|
include Yearbook
|
5
5
|
PIMGS = Dir.glob "#{File.expand_path '../spec/fixtures/images', '__FILE__'}/*.jpg"
|
6
6
|
|
7
|
+
=begin
|
7
8
|
img = Yearbook::Image.new(PIMGS.first)
|
8
9
|
img.write('test.jpg') do |i|
|
9
10
|
i.bw
|
10
|
-
|
11
|
+
i.resize_to_fit 200, 500
|
12
|
+
end
|
13
|
+
=end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
class AttHash < SimpleDelegator
|
3
|
+
def initialize
|
4
|
+
@hsh = {}
|
5
|
+
super(@hsh)
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(foo, *args, &blk)
|
9
|
+
if @hsh.respond_to?(foo)
|
10
|
+
@hsh.send(foo, *args, &blk)
|
11
|
+
else
|
12
|
+
@hsh[foo.to_sym] = args
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Yearbook
|
2
|
+
module Classifier
|
3
|
+
|
4
|
+
DATA_DIR = File.expand_path('../../../data/classifiers', __FILE__ )
|
5
|
+
DATA_FILES = {
|
6
|
+
faces: File.join(DATA_DIR, 'haarcascade_frontalface_default.xml')
|
7
|
+
|
8
|
+
}
|
9
|
+
|
10
|
+
|
11
|
+
# returns an array of detected objects
|
12
|
+
def self.detect_objects(cv_image, object_type)
|
13
|
+
detector = load_detector(object_type.to_sym)
|
14
|
+
|
15
|
+
detector.detect_objects(cv_image)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def self.load_detector(object_type)
|
20
|
+
fname = DATA_FILES[object_type]
|
21
|
+
puts fname
|
22
|
+
OpenCV::CvHaarClassifierCascade::load( fname )
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/yearbook/image.rb
CHANGED
@@ -1,62 +1,96 @@
|
|
1
1
|
require 'rmagick'
|
2
2
|
require 'hashie'
|
3
|
+
require_relative 'att_hash'
|
4
|
+
require_relative 'classifier'
|
3
5
|
|
4
6
|
|
5
7
|
|
6
|
-
require 'delegate'
|
7
|
-
class AttHash < SimpleDelegator
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
super(@hsh)
|
12
|
-
end
|
9
|
+
module Yearbook
|
10
|
+
class Image
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
@
|
17
|
-
|
18
|
-
@hsh[foo.to_sym] = args
|
12
|
+
attr_reader :filename
|
13
|
+
def initialize(fname)
|
14
|
+
@filename = fname
|
15
|
+
@objects = []
|
19
16
|
end
|
20
|
-
end
|
21
|
-
end
|
22
17
|
|
23
18
|
|
19
|
+
def detect_objects(obj_type)
|
20
|
+
@objects = Classifier.detect_objects(cv_object, obj_type).to_a
|
21
|
+
end
|
24
22
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def initialize(fname)
|
29
|
-
@magick_image = read_magick_image(fname)
|
23
|
+
def detect_faces
|
24
|
+
detect_objects(:faces)
|
30
25
|
end
|
31
26
|
|
27
|
+
def detected_objects
|
28
|
+
@objects
|
29
|
+
end
|
32
30
|
|
33
|
-
def write(
|
31
|
+
def write(base_out_fname, &blk)
|
34
32
|
klass = self.class
|
35
|
-
image_out = image_object
|
36
33
|
|
37
|
-
if
|
38
|
-
|
39
|
-
|
34
|
+
if @objects.empty?
|
35
|
+
img_objects = Array(image_object)
|
36
|
+
else
|
37
|
+
img_objects = @objects.map{|o| constitute_from_cv(o, image_object)}
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
img_objects.each_with_index do |image_out, idx|
|
41
|
+
if block_given?
|
42
|
+
h = AttHash.new
|
43
|
+
yield h
|
44
|
+
|
45
|
+
# transform the image
|
46
|
+
image_out = h.inject(image_out) do |img, (foo, args)|
|
47
|
+
klass.send(foo, img, *args)
|
48
|
+
end
|
44
49
|
end
|
45
|
-
end
|
46
50
|
|
47
|
-
|
51
|
+
if idx == 0
|
52
|
+
out_fname = base_out_fname
|
53
|
+
else
|
54
|
+
out_fname = base_out_fname.sub(/\.(?=\w+$)/, "-#{idx}.")
|
55
|
+
end
|
56
|
+
|
57
|
+
klass.output(image_out, out_fname)
|
58
|
+
end
|
48
59
|
end
|
49
60
|
|
50
61
|
|
62
|
+
|
63
|
+
|
64
|
+
|
51
65
|
private
|
52
|
-
def read_magick_image(fname)
|
53
|
-
Magick::Image::read(fname).first
|
54
|
-
end
|
55
66
|
|
67
|
+
|
68
|
+
# defer loading until it is needed
|
56
69
|
def image_object
|
57
|
-
@magick_image
|
70
|
+
@magick_image ||= load_magick_image(@filename)
|
58
71
|
end
|
59
72
|
|
73
|
+
def cv_object
|
74
|
+
@cv_image ||= load_cv_image(@filename)
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
def constitute_from_cv(c, img)
|
80
|
+
pixels = img.dispatch(c.x, c.y, c.width, c.height, "RGB")
|
81
|
+
|
82
|
+
return Magick::Image.constitute(c.width, c.height, "RGB", pixels)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def load_magick_image(fname)
|
87
|
+
Magick::Image::read(fname).first
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def load_cv_image(fname)
|
92
|
+
IplImage::load(fname)
|
93
|
+
end
|
60
94
|
|
61
95
|
|
62
96
|
# Image manipulation methods at the class level
|
data/spec/spec_helper.rb
CHANGED
data/spec/yearbook_spec.rb
CHANGED
@@ -1,8 +1,59 @@
|
|
1
|
-
require
|
1
|
+
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
module Yearbook
|
4
|
+
describe Image do
|
5
|
+
|
6
|
+
context 'instance' do
|
7
|
+
before do
|
8
|
+
@fname = File.join(IMAGE_DIR, 'bush-george-w.jpg')
|
9
|
+
@image = Image.new(@fname)
|
10
|
+
@tempfile = Tempfile.new 'bush.jpg'
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
@tempfile.unlink
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
describe 'initialization' do
|
19
|
+
it 'should have :filename reader' do
|
20
|
+
expect(@image.filename).to eq @fname
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#detect_faces' do
|
25
|
+
it 'should return an Array' do
|
26
|
+
expect( @image.detect_faces ).to be_an Array
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe '#write' do
|
31
|
+
it 'should write to disk' do
|
32
|
+
@image.write(@tempfile.path)
|
33
|
+
expect(@tempfile.size > 10000).to be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'after faces have been detected' do
|
37
|
+
before do
|
38
|
+
@image.detect_faces
|
39
|
+
end
|
8
40
|
|
41
|
+
it 'should write each object to disk' do
|
42
|
+
last_num = @image.detected_objects.count - 1
|
43
|
+
path = @tempfile.path
|
44
|
+
last_path = path.sub(/\.(?=\w+$)/, "-#{last_num}.")
|
45
|
+
|
46
|
+
@image.write(path)
|
47
|
+
expect(File.exists?(last_path)).to be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/yearbook.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "yearbook"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["dannguyen"]
|
12
|
-
s.date = "2013-11-
|
12
|
+
s.date = "2013-11-06"
|
13
13
|
s.description = "Easy face-cropping"
|
14
14
|
s.email = "dansonguyen@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -32,6 +32,8 @@ Gem::Specification.new do |s|
|
|
32
32
|
"data/classifiers/shameem_haarcascade_eye.xml",
|
33
33
|
"go.rb",
|
34
34
|
"lib/yearbook.rb",
|
35
|
+
"lib/yearbook/att_hash.rb",
|
36
|
+
"lib/yearbook/classifier.rb",
|
35
37
|
"lib/yearbook/image.rb",
|
36
38
|
"spec/fixtures/images/bush-george-w.jpg",
|
37
39
|
"spec/fixtures/images/obama-barack.jpg",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yearbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-11-
|
12
|
+
date: 2013-11-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rmagick
|
@@ -178,6 +178,8 @@ files:
|
|
178
178
|
- data/classifiers/shameem_haarcascade_eye.xml
|
179
179
|
- go.rb
|
180
180
|
- lib/yearbook.rb
|
181
|
+
- lib/yearbook/att_hash.rb
|
182
|
+
- lib/yearbook/classifier.rb
|
181
183
|
- lib/yearbook/image.rb
|
182
184
|
- spec/fixtures/images/bush-george-w.jpg
|
183
185
|
- spec/fixtures/images/obama-barack.jpg
|
@@ -203,7 +205,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
203
205
|
version: '0'
|
204
206
|
segments:
|
205
207
|
- 0
|
206
|
-
hash:
|
208
|
+
hash: -163145999741884676
|
207
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
208
210
|
none: false
|
209
211
|
requirements:
|