Created
April 11, 2011 14:09
-
-
Save ultraist/913574 to your computer and use it in GitHub Desktop.
AnimeFace-Rubyで検出した顔の目の色を変える
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# イラストの顔の目の色をクルクルするGIFアニメを作るコマンド | |
# AnimeFace-Ruby.tar.gzの最新版(2011/4/11 21時以降)が必要 | |
# | |
# ruby eyecolor.rb homu.jpg homu.gif | |
require "rubygems" | |
require "RMagick" | |
require "AnimeFace" | |
module AnimeFace | |
module EyeColor | |
def self.change(src, face, hsl, dest = nil) | |
dest ||= src.copy | |
change_eyecolor(dest, src, eye_info(face["eyes"]["left"]), hsl) | |
change_eyecolor(dest, src, eye_info(face["eyes"]["right"]), hsl) | |
dest | |
end | |
private | |
S = Magick::MaxRGB * 0.5 | |
B = 0.6 | |
A = 1.0 | |
def self.change_eyecolor(dest, src, eye, hsl) | |
rect = { | |
:x1 => [eye[:center][:x] - eye[:r] * 1.5, 0].max.to_i, | |
:y1 => [eye[:center][:y] - eye[:r] * 1.5, 0].max.to_i, | |
:x2 => [eye[:center][:x] + eye[:r] * 1.5, src.columns].min.to_i, | |
:y2 => [eye[:center][:y] + eye[:r] * 1.5, src.rows].min.to_i | |
} | |
(rect[:x1] ... rect[:x2]).each do |x| | |
(rect[:y1] ... rect[:y2]).each do |y| | |
p = src.pixel_color(x, y) | |
# 目の中心からの距離 | |
point_dist = Math.sqrt((x - eye[:center][:x]) * (x - eye[:center][:x]) + | |
(y - eye[:center][:y]) * (y - eye[:center][:y])) | |
# 目の色からの距離 | |
color_dist = nn_color_dist(p, eye[:color][0 ... 2]) | |
# 重みを計算して色相を回す | |
dw = gauss(point_dist, eye[:r] * B) | |
cw = gauss(color_dist, S) | |
dest.pixel_color(x, y, mulp(p, hsl, [(dw * cw) * A, 1.0].min)) | |
end | |
end | |
end | |
def self.gauss(d, sigma) | |
Math.exp(-(d * d) / (2.0 * sigma * sigma)) | |
end | |
def self.mulp(p1, hsl2, w) | |
hsl1 = p1.to_HSL | |
hue = Math.atan2((1.0 - w) * Math.sin(hsl1[0] * Math::PI * 2.0) + | |
w * Math.sin(hsl2[0] * Math::PI * 2.0), | |
(1.0 - w) * Math.cos(hsl1[0] * Math::PI * 2.0) + | |
w * Math.cos(hsl2[0] * Math::PI * 2.0)) / (Math::PI * 2.0) | |
if (hue < 0) | |
hue = 1.0 + hue | |
end | |
#saturation = ((1.0 - w) * hsl1[1] + w * hsl2[1]) | |
saturation = hsl1[1] | |
Magick::Pixel.from_HSL([hue, saturation, hsl1[2]]) | |
end | |
def self.nn_color_dist(x, data) | |
data.reduce(Magick::MaxRGB * 2) do |min_dist, v| | |
[min_dist, | |
Math.sqrt((x.red - v.red) ** 2 + | |
(x.green - v.green) ** 2 + | |
(x.blue - v.blue) ** 2) | |
].min | |
end | |
end | |
def self.eye_info(src) | |
dest = { | |
:x => src["x"], :y => src["y"], | |
:w => src["width"], :h => src["height"] | |
} | |
dest[:xr] = src["width"] / 2 | |
dest[:yr] = src["height"] / 2 | |
dest[:r] = [dest[:xr], dest[:yr]].max | |
dest[:center] = { | |
:x => src["x"] + dest[:xr], | |
:y => src["y"] + dest[:yr] | |
} | |
dest[:color] = src["colors"] | |
dest | |
end | |
end | |
end | |
def main(src, dest) | |
dest ||= "anime.gif" | |
image = Magick::ImageList.new(src) | |
gif = Magick::ImageList.new | |
results = AnimeFace.detect(image) | |
hue = results.map{ rand } | |
bg = image.first.copy | |
bg.delay = 0 | |
gif << bg | |
10.times do |i| | |
layer = Magick::Image.new(image.columns, image.rows) do | |
self.background_color = "none" | |
end | |
results.each_index do |j| | |
hue[j] = hue[j] > 0.9 ? hue[j] + 0.1 - 1.0 : hue[j] + 0.1 | |
AnimeFace::EyeColor.change(bg, results[j], | |
[hue[j], 0.3, 1.0], layer) | |
end | |
layer.delay = (Math.exp((-(i % 5) ** 2 ) / (2.0 * 4.0 ** 2)) * 10).to_i | |
gif << layer | |
end | |
gif.write(dest) | |
end | |
if (ARGV[0]) | |
main(ARGV[0], ARGV[1]) | |
else | |
warn "usage: #{$0} src [dest]" | |
exit(-1) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment