dealwithify 0.0.4
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/.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: []
|