dealwithify 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +3 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +10 -0
- data/Rakefile +2 -0
- data/bin/dealwithify +106 -0
- data/config.ru +2 -0
- data/dealwithify.gemspec +21 -0
- data/glasses.png +0 -0
- data/lib/dealwithify/version.rb +3 -0
- data/lib/dealwithify.rb +75 -0
- data/readme.md +32 -0
- metadata +73 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/Rakefile
ADDED
data/bin/dealwithify
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'dealwithify'
|
5
|
+
require 'net/http'
|
6
|
+
require 'open-uri'
|
7
|
+
require 'json'
|
8
|
+
require 'RMagick'
|
9
|
+
|
10
|
+
FACE_API_KEY = ENV['FACE_API_KEY']
|
11
|
+
FACE_API_SECRET = ENV['FACE_API_SECRET']
|
12
|
+
tmp_glasses = Magick::Image.read(File.join(File.expand_path(File.dirname(__FILE__)), '..', 'glasses.png')).first
|
13
|
+
tmp_glasses.format = 'gif'
|
14
|
+
GLASSES = tmp_glasses
|
15
|
+
|
16
|
+
ORIG_GLASSES_MID_X, ORIG_GLASSES_MID_Y = [210, 30]
|
17
|
+
FLIP_GLASSES_MID_X, FLIP_GLASSES_MID_Y = [115, 23]
|
18
|
+
GLASSES_WIDTH_MULTIPLIER = 2.7
|
19
|
+
|
20
|
+
def angle_between_eyes(left_eye, right_eye)
|
21
|
+
Math.atan((right_eye['y'] - left_eye['y'])/(right_eye['x'] - left_eye['x'])) * 180.0 / Math::PI
|
22
|
+
end
|
23
|
+
|
24
|
+
def rotate_point(center, point, angle)
|
25
|
+
cx, cy = center
|
26
|
+
px, py = point
|
27
|
+
|
28
|
+
distance = Math.sqrt((cx-px)**2 + (cy-py)**2)
|
29
|
+
|
30
|
+
[cx + Math.sin(Math::PI/180.0 * angle)*distance, cy + Math.cos(-Math::PI/180.0 * angle)*distance]
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an overlaid image (image1 over image2) such that the point at coords coords1 on image1
|
34
|
+
# are over the point at coords2 on image2
|
35
|
+
def align_images(image1, image2, coords1, coords2)
|
36
|
+
x1, y1 = coords1
|
37
|
+
x2, y2 = coords2
|
38
|
+
x_shift = x2 - x1
|
39
|
+
y_shift = y2 - y1
|
40
|
+
image2.composite(image1, x_shift, y_shift, Magick::OverCompositeOp)
|
41
|
+
end
|
42
|
+
|
43
|
+
img_src = ARGV[0]
|
44
|
+
if img_src
|
45
|
+
response = Net::HTTP.get(URI("http://api.face.com/faces/detect.json?api_key=#{FACE_API_KEY}&api_secret=#{FACE_API_SECRET}&urls=#{img_src}"))
|
46
|
+
parsed_response = JSON.parse(response)
|
47
|
+
|
48
|
+
tags = parsed_response['photos'].first['tags']
|
49
|
+
rotate = false #!(params[:rotate]=='false' || params[:rotate]=='no')
|
50
|
+
flip_flag = false #!(params[:flip]=='false' || params[:flip]=='no')
|
51
|
+
|
52
|
+
final = Magick::ImageList.new
|
53
|
+
|
54
|
+
original_image = Magick::Image.read(img_src).first
|
55
|
+
10.downto(1).each do |n|
|
56
|
+
cur_frame = original_image.dup
|
57
|
+
tags.each do |tag|
|
58
|
+
left_eye = tag['eye_left']
|
59
|
+
right_eye = tag['eye_right']
|
60
|
+
if left_eye && right_eye
|
61
|
+
eye_angle = angle_between_eyes(left_eye, right_eye)
|
62
|
+
flip = tag['yaw'] > 0 && flip_flag
|
63
|
+
cur_frame = DealWithify::DealWithIt.new(left_eye, right_eye, eye_angle, cur_frame, rotate, flip).shifted_glasses(-10 * n)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
final << cur_frame
|
67
|
+
end
|
68
|
+
|
69
|
+
centered_frame = Magick::Image.read(img_src).first
|
70
|
+
tags.each do |tag|
|
71
|
+
left_eye = tag['eye_left']
|
72
|
+
right_eye = tag['eye_right']
|
73
|
+
if left_eye && right_eye
|
74
|
+
eye_angle = angle_between_eyes(left_eye, right_eye)
|
75
|
+
flip = tag['yaw'] > 0 && flip_flag
|
76
|
+
centered_frame = DealWithify::DealWithIt.new(left_eye, right_eye, eye_angle, centered_frame, rotate, flip).centered_glasses
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
txt = Magick::Draw.new
|
81
|
+
centered_frame = centered_frame.annotate(txt, 0,0,0,0, 'Deal with it'){
|
82
|
+
txt.font_family = 'monospace'
|
83
|
+
txt.gravity = Magick::SouthGravity
|
84
|
+
txt.pointsize = 36
|
85
|
+
txt.stroke = '#ffffff'
|
86
|
+
txt.stroke_width = 2
|
87
|
+
txt.fill = '#000000'
|
88
|
+
txt.font_weight = Magick::BoldWeight
|
89
|
+
}
|
90
|
+
|
91
|
+
5.times { final << centered_frame.dup }
|
92
|
+
|
93
|
+
#final.to_blob
|
94
|
+
img_uri = URI.parse(img_src)
|
95
|
+
file = img_uri.path.split('/')[-1]
|
96
|
+
filename = file.chomp(File.extname(file))
|
97
|
+
final.write("#{filename}.gif")
|
98
|
+
else
|
99
|
+
puts <<-eos
|
100
|
+
Give it a url!
|
101
|
+
dealwithify url
|
102
|
+
|
103
|
+
example:
|
104
|
+
dealwithify http://facebook.com/profile.jpg
|
105
|
+
eos
|
106
|
+
end
|
data/config.ru
ADDED
data/dealwithify.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'dealwithify/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |g|
|
6
|
+
g.name = "dealwithify"
|
7
|
+
g.version = DealWithify::VERSION
|
8
|
+
g.platform = Gem::Platform::RUBY
|
9
|
+
g.authors = ["Tomer Elmalem"]
|
10
|
+
g.email = ["telmalem@gmail.com"]
|
11
|
+
g.homepage = "https://github.com/tomelm/deal-withify"
|
12
|
+
g.summary = ""
|
13
|
+
g.description = %q{CLI for Dealwithify}
|
14
|
+
|
15
|
+
g.files = `git ls-files`.split("\n")
|
16
|
+
g.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
g.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
18
|
+
g.require_paths = ["lib"]
|
19
|
+
|
20
|
+
g.add_dependency 'rmagick'
|
21
|
+
end
|
data/glasses.png
ADDED
Binary file
|
data/lib/dealwithify.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'dealwithify/version'
|
2
|
+
|
3
|
+
module DealWithify
|
4
|
+
class DealWithIt
|
5
|
+
def initialize(left_eye, right_eye, angle, img, rotate, flip)
|
6
|
+
@img = img
|
7
|
+
@img.format = 'gif'
|
8
|
+
@rotate = rotate
|
9
|
+
@flip = flip
|
10
|
+
@img_width = @img.columns
|
11
|
+
@img_height = @img.rows
|
12
|
+
@saved_glasses = nil
|
13
|
+
|
14
|
+
left_eye_x = left_eye['x'] * @img_width / 100.0
|
15
|
+
left_eye_y = left_eye['y'] * @img_height / 100.0
|
16
|
+
right_eye_x = right_eye['x'] * @img_width / 100.0
|
17
|
+
right_eye_y = right_eye['y'] * @img_height / 100.0
|
18
|
+
|
19
|
+
@eye_distance = Math.sqrt((left_eye_x - right_eye_x)**2 + (left_eye_y - right_eye_y)**2)
|
20
|
+
eye_midpoint_x = left_eye_x + (right_eye_x - left_eye_x)/2.0
|
21
|
+
eye_midpoint_y = left_eye_y + (right_eye_y - left_eye_y)/2.0
|
22
|
+
@eye_midpoint = [eye_midpoint_x, eye_midpoint_y]
|
23
|
+
@angle = angle
|
24
|
+
end
|
25
|
+
|
26
|
+
def shrunk_glasses_dimensions
|
27
|
+
new_width = @eye_distance * GLASSES_WIDTH_MULTIPLIER
|
28
|
+
scale = new_width / GLASSES.columns.to_f
|
29
|
+
new_height = scale * GLASSES.rows.to_f
|
30
|
+
[new_width, new_height]
|
31
|
+
end
|
32
|
+
|
33
|
+
def scaled_and_rotated_glasses
|
34
|
+
return @saved_glasses if @saved_glasses
|
35
|
+
new_glasses = GLASSES.resize(*self.shrunk_glasses_dimensions, Magick::TriangleFilter, 0.5)
|
36
|
+
new_glasses.background_color = 'none'
|
37
|
+
new_glasses.format = 'png'
|
38
|
+
|
39
|
+
new_glasses.flop! if @flip
|
40
|
+
new_glasses.rotate!(@angle) if @rotate
|
41
|
+
|
42
|
+
@saved_glasses = new_glasses
|
43
|
+
end
|
44
|
+
|
45
|
+
def glasses_midpoint
|
46
|
+
gwidth, gheight = self.shrunk_glasses_dimensions
|
47
|
+
scale = gwidth / GLASSES.columns.to_f
|
48
|
+
if @flip
|
49
|
+
[FLIP_GLASSES_MID_X * scale, FLIP_GLASSES_MID_Y * scale]
|
50
|
+
else
|
51
|
+
[ORIG_GLASSES_MID_X * scale, ORIG_GLASSES_MID_Y * scale]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def scaled_and_rotated_glasses_midpoint
|
56
|
+
midx, midy = self.glasses_midpoint
|
57
|
+
if @rotate
|
58
|
+
# midx += 50 * Math.cos(@angle * Math::PI/180)
|
59
|
+
# This pushes the glasses up the nose a bit.
|
60
|
+
midy += (50 * Math.sin(@angle * Math::PI/180)).abs
|
61
|
+
end
|
62
|
+
[midx, midy]
|
63
|
+
end
|
64
|
+
|
65
|
+
def centered_glasses
|
66
|
+
align_images(self.scaled_and_rotated_glasses, @img, self.scaled_and_rotated_glasses_midpoint, @eye_midpoint)
|
67
|
+
end
|
68
|
+
|
69
|
+
def shifted_glasses(y_shift)
|
70
|
+
glasses_mid_x, glasses_mid_y = self.scaled_and_rotated_glasses_midpoint
|
71
|
+
glasses_mid_y -= y_shift
|
72
|
+
align_images(self.scaled_and_rotated_glasses, @img, [glasses_mid_x, glasses_mid_y], @eye_midpoint)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/readme.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
DealWithify CLI
|
2
|
+
===============
|
3
|
+
Command line interface for the Dealwithify app by Brian Stanwyck.
|
4
|
+
|
5
|
+
Prerequisites
|
6
|
+
-------------
|
7
|
+
* ImageMagick + RMagick
|
8
|
+
|
9
|
+
* Face.com API key and API secret set to environment variables
|
10
|
+
|
11
|
+
Usage
|
12
|
+
-----
|
13
|
+
Install the gem
|
14
|
+
|
15
|
+
gem install dealwithify
|
16
|
+
|
17
|
+
Run!
|
18
|
+
|
19
|
+
dealwithify https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash3/579380_3706063060148_602358919_n.jpg
|
20
|
+
|
21
|
+
This will create a .gif in your local directory that you will need to upload somewhere!
|
22
|
+
|
23
|
+
Result
|
24
|
+
|
25
|
+
http://i.imgur.com/A5lZ4.gif
|
26
|
+
|
27
|
+
Setting environment variables
|
28
|
+
-----------------------------
|
29
|
+
Paste these two lines into your .bashrc or .zshrc
|
30
|
+
|
31
|
+
export FACE_API_KEY = 'your api key'
|
32
|
+
export FACE_API_SECRET = 'your api secret'
|
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: dealwithify
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Tomer Elmalem
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rmagick
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: CLI for Dealwithify
|
31
|
+
email:
|
32
|
+
- telmalem@gmail.com
|
33
|
+
executables:
|
34
|
+
- dealwithify
|
35
|
+
extensions: []
|
36
|
+
extra_rdoc_files: []
|
37
|
+
files:
|
38
|
+
- .gitignore
|
39
|
+
- Gemfile
|
40
|
+
- Gemfile.lock
|
41
|
+
- Rakefile
|
42
|
+
- bin/dealwithify
|
43
|
+
- config.ru
|
44
|
+
- dealwithify.gemspec
|
45
|
+
- glasses.png
|
46
|
+
- lib/dealwithify.rb
|
47
|
+
- lib/dealwithify/version.rb
|
48
|
+
- readme.md
|
49
|
+
homepage: https://github.com/tomelm/deal-withify
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.8.24
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: ''
|
73
|
+
test_files: []
|