yearbook 0.2.3 → 0.3.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/README.md +30 -1
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/bin/yearbook +6 -0
- data/lib/yearbook.rb +2 -1
- data/lib/yearbook/att_hash.rb +2 -0
- data/lib/yearbook/classifier.rb +24 -9
- data/lib/yearbook/cli.rb +26 -0
- data/lib/yearbook/detected_object.rb +26 -0
- data/lib/yearbook/detection.rb +13 -0
- data/lib/yearbook/detection/collection.rb +20 -0
- data/lib/yearbook/detector.rb +21 -0
- data/lib/yearbook/image.rb +98 -63
- data/lib/yearbook/manipulator.rb +33 -0
- data/spec/yearbook_spec.rb +103 -16
- data/yearbook.gemspec +10 -2
- metadata +12 -4
data/README.md
CHANGED
@@ -1 +1,30 @@
|
|
1
|
-
|
1
|
+
# Yearbook
|
2
|
+
|
3
|
+
A convenience for cropping and outputting images, by Dan Nguyen ([@dancow](https://twitter.com/dancow))
|
4
|
+
|
5
|
+
A very thin wrapper around [ruby-opencv](https://github.com/ruby-opencv/ruby-opencv) and [rmagick](http://rmagick.rubyforge.org/) (which themselves are wrappers around [OpenCV](http://opencv.org/) and [ImageMagick](http://www.imagemagick.org) + [GraphicsMagick](http://www.graphicsmagick.org/)) for producing face-cropped images from a source image.
|
6
|
+
|
7
|
+
This is in very-early stage...I'm using it mostly as a quickie image cropper for various projects, as well as a way to learn the cool [thor](https://github.com/erikhuda/thor) gem.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Load all the dependencies mentioned above (ha!)
|
12
|
+
|
13
|
+
Then:
|
14
|
+
|
15
|
+
`gem install yearbook`
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### In Ruby
|
20
|
+
|
21
|
+
require 'yearbook'
|
22
|
+
|
23
|
+
image = Yearbook::Image.new('path/to/somebody.jpg')
|
24
|
+
image.clip_and_print_best_face('output/to/somebodys-face.jpg')
|
25
|
+
|
26
|
+
### Command-line tool
|
27
|
+
|
28
|
+
yearbook face george-washington.jpg
|
29
|
+
# will print out to "george-washington-face.jpg" by default
|
30
|
+
|
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
4
|
|
4
5
|
require 'rubygems'
|
5
6
|
require 'bundler'
|
@@ -23,6 +24,7 @@ Jeweler::Tasks.new do |gem|
|
|
23
24
|
gem.email = "dansonguyen@gmail.com"
|
24
25
|
gem.authors = ["dannguyen"]
|
25
26
|
gem.files.exclude 'spec/fixtures/images/*.jpg' # exclude temporary directory
|
27
|
+
gem.executables << 'yearbook'
|
26
28
|
|
27
29
|
# dependencies defined in Gemfile
|
28
30
|
end
|
@@ -36,7 +38,6 @@ end
|
|
36
38
|
|
37
39
|
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
38
40
|
spec.pattern = 'spec/**/*_spec.rb'
|
39
|
-
spec.rcov = true
|
40
41
|
end
|
41
42
|
|
42
43
|
task :default => :spec
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.3.0
|
data/bin/yearbook
ADDED
data/lib/yearbook.rb
CHANGED
data/lib/yearbook/att_hash.rb
CHANGED
data/lib/yearbook/classifier.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
1
3
|
module Yearbook
|
2
|
-
|
4
|
+
class Classifier
|
3
5
|
|
4
6
|
DATA_DIR = File.expand_path('../../../data/classifiers', __FILE__ )
|
5
7
|
DATA_FILES = {
|
@@ -7,20 +9,33 @@ module Yearbook
|
|
7
9
|
|
8
10
|
}
|
9
11
|
|
12
|
+
attr_reader :filename
|
13
|
+
|
14
|
+
def initialize(fname)
|
15
|
+
@filename = fname
|
10
16
|
|
11
|
-
|
12
|
-
|
13
|
-
detector = load_detector(object_type.to_sym)
|
17
|
+
@classifier = load_classifier(@filename)
|
18
|
+
end
|
14
19
|
|
15
|
-
|
20
|
+
def detect_objects(cvimg)
|
21
|
+
@classifier.detect_objects(cvimg)
|
16
22
|
end
|
17
23
|
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
25
|
+
|
26
|
+
|
27
|
+
# convenience
|
28
|
+
# will also pluralize anything
|
29
|
+
def self.of(object_type)
|
30
|
+
object_name = object_type.to_s.pluralize.to_sym
|
31
|
+
|
32
|
+
return self.new DATA_FILES[object_name]
|
23
33
|
end
|
24
34
|
|
35
|
+
|
36
|
+
private
|
37
|
+
def load_classifier(fname)
|
38
|
+
OpenCV::CvHaarClassifierCascade::load(fname )
|
39
|
+
end
|
25
40
|
end
|
26
41
|
end
|
data/lib/yearbook/cli.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Yearbook
|
4
|
+
class CLI < Thor
|
5
|
+
desc 'hello', 'from'
|
6
|
+
def hello(from)
|
7
|
+
puts "Hello world, from: #{from}"
|
8
|
+
end
|
9
|
+
|
10
|
+
desc 'face',
|
11
|
+
%q{ Provide the path to an image with a face in it:
|
12
|
+
yearbook face /path/to/photo.jpg /path/out/face-in-photo.jpg
|
13
|
+
}
|
14
|
+
def face(path, outpath=nil)
|
15
|
+
fpath = File.expand_path(path)
|
16
|
+
@image = Image.new(fpath)
|
17
|
+
|
18
|
+
outpath ||= path.sub(/(?=\.\w*$)/, '-face')
|
19
|
+
# eh overwrite it anyway
|
20
|
+
@image.clip_and_print_best_face(outpath)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# NOT USED YET
|
2
|
+
# module Yearbook
|
3
|
+
# class DetectedObject
|
4
|
+
|
5
|
+
# attr_reader @x, @y, @width, @height
|
6
|
+
|
7
|
+
# # args can either be one CvMat object,
|
8
|
+
# # or x,y,w,h dimensions
|
9
|
+
# def initialize(*args)
|
10
|
+
|
11
|
+
# case args.count
|
12
|
+
# when 1
|
13
|
+
# # CVMat
|
14
|
+
# when 4
|
15
|
+
# @x = x
|
16
|
+
# @y = y
|
17
|
+
# @width = w
|
18
|
+
# @height = h
|
19
|
+
# else
|
20
|
+
# raise "Expected 1 or 4 arguments, not #{args.count}"
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
|
24
|
+
|
25
|
+
# end
|
26
|
+
# end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module Yearbook
|
4
|
+
module Detection
|
5
|
+
class Collection < SimpleDelegator
|
6
|
+
def initialize(cvseq, opts={})
|
7
|
+
@cv_seq = cvseq
|
8
|
+
|
9
|
+
super(@cv_seq)
|
10
|
+
end
|
11
|
+
|
12
|
+
# returns just one of the CvAvgComps
|
13
|
+
def best
|
14
|
+
sort_by{|c| c.width * c.height}.reverse.first
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'classifier'
|
2
|
+
require_relative 'detection'
|
3
|
+
|
4
|
+
module Yearbook
|
5
|
+
module Detector
|
6
|
+
|
7
|
+
class << self
|
8
|
+
# returns an array of detected objects
|
9
|
+
def detect_objects(cv_image, object_type)
|
10
|
+
x = Classifier.of(object_type).detect_objects(cv_image)
|
11
|
+
|
12
|
+
|
13
|
+
return Yearbook.DetectionCollection(x, object_type)
|
14
|
+
end
|
15
|
+
|
16
|
+
def load_cv(fname)
|
17
|
+
IplImage::load(fname)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/yearbook/image.rb
CHANGED
@@ -1,115 +1,150 @@
|
|
1
|
-
require 'rmagick'
|
2
|
-
require 'hashie'
|
3
1
|
require_relative 'att_hash'
|
4
|
-
require_relative '
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
require_relative 'manipulator'
|
3
|
+
require_relative 'detector'
|
8
4
|
|
9
5
|
module Yearbook
|
10
6
|
class Image
|
11
7
|
|
12
|
-
attr_reader :filename
|
8
|
+
attr_reader :filename, :clips
|
13
9
|
def initialize(fname)
|
14
|
-
@filename = fname
|
15
|
-
@objects = []
|
10
|
+
@filename = fname
|
16
11
|
end
|
17
12
|
|
18
13
|
|
19
|
-
|
20
|
-
|
14
|
+
|
15
|
+
# args is empty for now...
|
16
|
+
def clip(obj_type, *args, &blk)
|
17
|
+
@clips = detect_and_collect(cv_image, obj_type)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
|
23
|
+
def clipped?; @clips.count > 0; end
|
24
|
+
|
25
|
+
# just the original image
|
26
|
+
def print(fname, &blk)
|
27
|
+
write_images(magick_image, fname, &blk)
|
21
28
|
end
|
22
29
|
|
23
|
-
|
24
|
-
|
30
|
+
# print all the clips
|
31
|
+
def print_clips(fname, &blk)
|
32
|
+
write_images(constitute_clips, fname, &blk)
|
25
33
|
end
|
26
34
|
|
27
|
-
def
|
28
|
-
|
35
|
+
def print_best_clip(fname, &blk)
|
36
|
+
write_images(constitute_best_clip, fname, &blk)
|
29
37
|
end
|
30
38
|
|
31
|
-
|
32
|
-
|
39
|
+
# convenience method
|
40
|
+
def clip_and_print(obj_type, filename, *args, &blk)
|
41
|
+
clip(obj_type, *args, &blk)
|
33
42
|
|
34
|
-
|
35
|
-
|
36
|
-
else
|
37
|
-
img_objects = @objects.map{|o| constitute_from_cv(o, image_object)}
|
38
|
-
end
|
43
|
+
print_clips(filename)
|
44
|
+
end
|
39
45
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
yield h
|
46
|
+
# convenience method
|
47
|
+
def clip_and_print_best(obj_type, filename, *args, &blk)
|
48
|
+
clip(obj_type, *args, &blk)
|
44
49
|
|
45
|
-
|
46
|
-
|
47
|
-
klass.send(foo, img, *args)
|
48
|
-
end
|
49
|
-
end
|
50
|
+
print_best_clip(filename)
|
51
|
+
end
|
50
52
|
|
51
|
-
if idx == 0
|
52
|
-
out_fname = base_out_fname
|
53
|
-
else
|
54
|
-
out_fname = base_out_fname.sub(/\.(?=\w+$)/, "-#{idx}.")
|
55
|
-
end
|
56
53
|
|
57
|
-
|
58
|
-
|
54
|
+
### META STUFF
|
55
|
+
|
56
|
+
CLIPPER_REGEX = /^(clip_and_print(?:_best)?|clip)_(\w+)$/
|
57
|
+
def method_missing(foo, *args, &blk)
|
58
|
+
f = foo.to_s
|
59
|
+
# e.g. clip_faces
|
60
|
+
if f =~ CLIPPER_REGEX
|
61
|
+
self.send $1, $2, *args, &blk
|
62
|
+
else
|
63
|
+
super
|
64
|
+
end
|
59
65
|
end
|
60
66
|
|
67
|
+
def respond_to?(foo, include_private=false)
|
68
|
+
return foo.to_s =~ CLIPPER_REGEX ? true : super(foo, include_private)
|
69
|
+
end
|
61
70
|
|
62
71
|
|
63
72
|
|
64
73
|
|
65
74
|
private
|
66
75
|
|
76
|
+
def best_clip
|
77
|
+
@clips.best
|
78
|
+
end
|
67
79
|
|
68
|
-
|
69
|
-
|
70
|
-
@magick_image ||= load_magick_image(@filename)
|
80
|
+
def constitute_from_cv(cv, img)
|
81
|
+
Manipulator.constitute(img, cv.x, cv.y, cv.width, cv.height)
|
71
82
|
end
|
72
83
|
|
73
|
-
def
|
74
|
-
|
84
|
+
def constitute(objects)
|
85
|
+
objs = Array(objects)
|
86
|
+
objs.map{|o| constitute_from_cv(o, magick_image)}
|
75
87
|
end
|
76
88
|
|
89
|
+
# just a helper
|
90
|
+
def constitute_clips
|
91
|
+
constitute(@clips)
|
92
|
+
end
|
77
93
|
|
94
|
+
def constitute_best_clip
|
95
|
+
constitute(best_clip)
|
96
|
+
end
|
78
97
|
|
79
|
-
def
|
80
|
-
|
98
|
+
def cv_image
|
99
|
+
@_cv_image ||= Detector.load_cv(@filename)
|
100
|
+
end
|
81
101
|
|
82
|
-
|
102
|
+
def detect_and_collect(cv, obj_type)
|
103
|
+
Detector.detect_objects(cv, obj_type)
|
104
|
+
end
|
105
|
+
|
106
|
+
# defer loading until it is needed
|
107
|
+
def magick_image
|
108
|
+
@_magick_image ||= Manipulator.load_magick(@filename)
|
83
109
|
end
|
84
110
|
|
85
111
|
|
86
|
-
|
87
|
-
|
88
|
-
|
112
|
+
# returns a manipulated Magick file
|
113
|
+
def transform_image(obj, &blk)
|
114
|
+
transformed_obj = obj
|
115
|
+
if block_given?
|
116
|
+
method_queue = AttHash.new
|
117
|
+
yield method_queue
|
89
118
|
|
119
|
+
# transform the image
|
120
|
+
transformed_obj = method_queue.inject(transformed_obj) do |img, (foo, args)|
|
121
|
+
Manipulator.send(foo, img, *args)
|
122
|
+
end
|
123
|
+
end
|
90
124
|
|
91
|
-
|
92
|
-
IplImage::load(fname)
|
125
|
+
return transformed_obj
|
93
126
|
end
|
94
127
|
|
128
|
+
def write_images(magick_objects, base_out_fname, &blk)
|
95
129
|
|
96
|
-
|
97
|
-
# no reason for them to be instance methods
|
98
|
-
class << self
|
99
|
-
def bw(img, num_colors = 128)
|
100
|
-
img.quantize(num_colors, Magick::GRAYColorspace)
|
101
|
-
end
|
130
|
+
arr = Array(magick_objects)
|
102
131
|
|
103
|
-
|
104
|
-
|
132
|
+
arr.each_with_index do |obj, idx|
|
133
|
+
output_img = transform_image(obj, &blk)
|
134
|
+
# generate a numbered filename if there are more than one
|
135
|
+
output_fname = arr.count == 1 ? base_out_fname : base_out_fname.sub(/\.(?=\w+$)/, "-#{idx}.")
|
105
136
|
|
106
|
-
|
137
|
+
write_image(output_img, output_fname)
|
107
138
|
end
|
139
|
+
end
|
108
140
|
|
109
|
-
|
110
|
-
|
111
|
-
|
141
|
+
|
142
|
+
# img is expected to be a Magick Object
|
143
|
+
def write_image(img, out_fname)
|
144
|
+
img.write(out_fname)
|
112
145
|
end
|
113
146
|
|
147
|
+
|
148
|
+
|
114
149
|
end
|
115
150
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rmagick'
|
2
|
+
|
3
|
+
module Yearbook
|
4
|
+
module Manipulator
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def bw(img, num_colors = 128)
|
8
|
+
img.quantize(num_colors, Magick::GRAYColorspace)
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def constitute(img, *args)
|
13
|
+
x,y,w,h = args[0..3]
|
14
|
+
pixels = img.dispatch(x, y, w, h, "RGB")
|
15
|
+
|
16
|
+
Magick::Image.constitute(w, h, "RGB", pixels)
|
17
|
+
end
|
18
|
+
|
19
|
+
def resize_to_fit(img, w, h = nil)
|
20
|
+
h ||= w
|
21
|
+
|
22
|
+
img.resize_to_fit(w, h)
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def load_magick(fname)
|
27
|
+
Magick::Image::read(fname).first
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/yearbook_spec.rb
CHANGED
@@ -21,39 +21,126 @@ module Yearbook
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
24
|
+
context 'clipping' do
|
25
|
+
before(:each) do
|
26
|
+
@image.clip_faces
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should be #clipped?' do
|
30
|
+
expect(@image).to be_clipped
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '@clips' do
|
34
|
+
it 'should return an Detection::Collection' do
|
35
|
+
expect( @image.clips ).to be_a Yearbook::Detection::Collection
|
36
|
+
end
|
27
37
|
end
|
28
38
|
end
|
29
39
|
|
30
|
-
describe '
|
31
|
-
|
32
|
-
|
33
|
-
|
40
|
+
describe 'printing' do
|
41
|
+
describe '#print' do
|
42
|
+
it 'should write the same file to disk' do
|
43
|
+
@image.print(@tempfile.path)
|
44
|
+
expect(@tempfile.size > 10000).to be_true
|
45
|
+
end
|
34
46
|
end
|
35
47
|
|
36
|
-
|
48
|
+
describe '#print_clips' do
|
49
|
+
context 'after faces have been clipped' do
|
50
|
+
before do
|
51
|
+
@image.clip_faces
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should print each object to disk' do
|
55
|
+
temp_path = @tempfile.path
|
56
|
+
@image.print_clips(temp_path)
|
57
|
+
@image.clips.each_with_index do |idx|
|
58
|
+
clip_path = temp_path.sub(/\.(?=\w+$)/, "-#{idx}.")
|
59
|
+
expect(File.exists?(clip_path)).to be_true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#print_best_clip' do
|
37
66
|
before do
|
38
|
-
@image.
|
67
|
+
@image.clip_faces
|
68
|
+
@image.print_best_clip(@tempfile.path)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should print the one face' do
|
72
|
+
expect(File.exists?@tempfile.path).to be_true
|
39
73
|
end
|
40
74
|
|
41
|
-
it 'should
|
42
|
-
|
43
|
-
|
44
|
-
|
75
|
+
it 'that face should be the biggest face' do
|
76
|
+
best_face = @image.send(:best_clip)
|
77
|
+
|
78
|
+
# icky method, requires using best_clip private method
|
79
|
+
# AND knowing default Detection::Collection :best sorting
|
45
80
|
|
46
|
-
|
47
|
-
|
81
|
+
pixel_count = best_face.width * best_face.height
|
82
|
+
magick_image = Manipulator.load_magick(@tempfile.path)
|
83
|
+
|
84
|
+
expect(pixel_count).to eq magick_image.columns * magick_image.rows
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'meta-conveniences' do
|
89
|
+
it 'should have #clip_and_print_faces' do
|
90
|
+
pending 'better path'
|
91
|
+
@image.clip_and_print_faces(@tempfile.path)
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'should have #clip_and_print_best_face' do
|
95
|
+
# note how tolerant it is of singular things
|
96
|
+
@image.clip_and_print_best_face(@tempfile.path)
|
97
|
+
best_face = @image.send(:best_clip)
|
98
|
+
|
99
|
+
# redux test
|
100
|
+
# icky method, requires using best_clip private method
|
101
|
+
# AND knowing default Detection::Collection :best sorting
|
102
|
+
|
103
|
+
pixel_count = best_face.width * best_face.height
|
104
|
+
magick_image = Manipulator.load_magick(@tempfile.path)
|
105
|
+
|
106
|
+
|
107
|
+
binding.pry
|
108
|
+
expect(pixel_count).to eq magick_image.columns * magick_image.rows
|
48
109
|
end
|
49
110
|
|
50
111
|
end
|
51
112
|
end
|
113
|
+
|
114
|
+
context 'cropping' do
|
115
|
+
it 'should allow close cropping'
|
116
|
+
it 'should take in percentages and weights'
|
117
|
+
# crop_to_object -10, -20
|
118
|
+
end
|
52
119
|
|
53
120
|
|
121
|
+
it 'should' do
|
122
|
+
pending 'what'
|
123
|
+
@image.clipped? # to eq true
|
124
|
+
@image.clips # to be an array
|
125
|
+
@image.clips # to eq CV thingy
|
54
126
|
|
55
|
-
|
127
|
+
# # API:
|
128
|
+
# img = @image.new(@fname)
|
129
|
+
# img.clip_face
|
130
|
+
# img.write do |f|
|
131
|
+
# # etc do |f|
|
132
|
+
# f.clip_face # -d face
|
133
|
+
# f.bw # --bw
|
134
|
+
# f.resize_to_fill(100, 200) # --fill 100,200
|
135
|
+
# f.write(whatev)
|
136
|
+
# end
|
56
137
|
|
138
|
+
# yearbook photo.jpg face.jpg --clip face
|
139
|
+
|
140
|
+
end
|
57
141
|
|
142
|
+
|
143
|
+
|
144
|
+
end
|
58
145
|
end
|
59
146
|
end
|
data/yearbook.gemspec
CHANGED
@@ -5,13 +5,14 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "yearbook"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.3.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-09"
|
13
13
|
s.description = "Easy face-cropping"
|
14
14
|
s.email = "dansonguyen@gmail.com"
|
15
|
+
s.executables = ["yearbook", "yearbook"]
|
15
16
|
s.extra_rdoc_files = [
|
16
17
|
"LICENSE.txt",
|
17
18
|
"README.md"
|
@@ -25,6 +26,7 @@ Gem::Specification.new do |s|
|
|
25
26
|
"README.md",
|
26
27
|
"Rakefile",
|
27
28
|
"VERSION",
|
29
|
+
"bin/yearbook",
|
28
30
|
"data/classifiers/eye-left-ojoI.xml",
|
29
31
|
"data/classifiers/frontalEyes35x16.xml",
|
30
32
|
"data/classifiers/haarcascade_frontalface_alt.xml",
|
@@ -34,7 +36,13 @@ Gem::Specification.new do |s|
|
|
34
36
|
"lib/yearbook.rb",
|
35
37
|
"lib/yearbook/att_hash.rb",
|
36
38
|
"lib/yearbook/classifier.rb",
|
39
|
+
"lib/yearbook/cli.rb",
|
40
|
+
"lib/yearbook/detected_object.rb",
|
41
|
+
"lib/yearbook/detection.rb",
|
42
|
+
"lib/yearbook/detection/collection.rb",
|
43
|
+
"lib/yearbook/detector.rb",
|
37
44
|
"lib/yearbook/image.rb",
|
45
|
+
"lib/yearbook/manipulator.rb",
|
38
46
|
"spec/spec_helper.rb",
|
39
47
|
"spec/yearbook_spec.rb",
|
40
48
|
"yearbook.gemspec"
|
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.3.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-09 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rmagick
|
@@ -157,7 +157,8 @@ dependencies:
|
|
157
157
|
version: '0'
|
158
158
|
description: Easy face-cropping
|
159
159
|
email: dansonguyen@gmail.com
|
160
|
-
executables:
|
160
|
+
executables:
|
161
|
+
- yearbook
|
161
162
|
extensions: []
|
162
163
|
extra_rdoc_files:
|
163
164
|
- LICENSE.txt
|
@@ -171,6 +172,7 @@ files:
|
|
171
172
|
- README.md
|
172
173
|
- Rakefile
|
173
174
|
- VERSION
|
175
|
+
- bin/yearbook
|
174
176
|
- data/classifiers/eye-left-ojoI.xml
|
175
177
|
- data/classifiers/frontalEyes35x16.xml
|
176
178
|
- data/classifiers/haarcascade_frontalface_alt.xml
|
@@ -180,7 +182,13 @@ files:
|
|
180
182
|
- lib/yearbook.rb
|
181
183
|
- lib/yearbook/att_hash.rb
|
182
184
|
- lib/yearbook/classifier.rb
|
185
|
+
- lib/yearbook/cli.rb
|
186
|
+
- lib/yearbook/detected_object.rb
|
187
|
+
- lib/yearbook/detection.rb
|
188
|
+
- lib/yearbook/detection/collection.rb
|
189
|
+
- lib/yearbook/detector.rb
|
183
190
|
- lib/yearbook/image.rb
|
191
|
+
- lib/yearbook/manipulator.rb
|
184
192
|
- spec/spec_helper.rb
|
185
193
|
- spec/yearbook_spec.rb
|
186
194
|
- yearbook.gemspec
|
@@ -199,7 +207,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
199
207
|
version: '0'
|
200
208
|
segments:
|
201
209
|
- 0
|
202
|
-
hash:
|
210
|
+
hash: 277460540779661417
|
203
211
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
204
212
|
none: false
|
205
213
|
requirements:
|