Λάδι Βιώσας

http://profile.hatena.ne.jp/kenkitii/

MacOSX に OpenCV を入れて ruby で遊んでみる2

昨日の続きです。

# -*- coding: utf-8 -*-
#! /usr/bin/env ruby
require 'rubygems'
require 'detector'
require 'RMagick'

file = ARGV.shift
model = '/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml'
raise "Arguments error" unless file
raise "File not found: #{file}" unless File.exists?(file)
raise "File not found: #{model}" unless File.exists?(model)

d = Magick::Draw.new.stroke('tomato').stroke_width(4).fill_opacity(0)
Detector::detect(model, file).map{|p| x, y, w, h = p; d.rectangle(x, y ,x+w, y+h) }

image = Magick::ImageList.new(file)
d.draw(image)
image.write('output.jpg')

とりあえず、↑こんなコード書いて動かしてみた。検出できた顔の部分を赤い線で囲ってみる奴です。手持ちの画像で試した結果はこんな感じ↓

や、、、別に平野綾なんとかのファンとかいうんじゃないので誤解しないでくださいね。マイ画像フォルダの中みたら、実験向きな画像がたまたま入ってただけです、たまたま。

さて、他にも色々な画像で試してみたところ、まぁ上の結果にも表れてますが、結構、顔検出してくれないパターンがあるんですよね。斜め向いてる顔を検出しないのはどうしようもないですけど、正面向いてる顔でも目の位置が水平でない場合(顔を傾けてる)はダメっぽい。うーん、、、

例えば、こんな画像↓は全く検出してくれないんですよ。

あ、、、別にこういう↑DVDばっかりみてるとかいうんじゃないので誤解しないでくださいね。顔を傾けてる画像を探してたら、たまたまとあるサイトで実験向きの画像をみつけただけです。ていうか、いちいち言い訳しないといけない画像を使うな、ってことですね、すいません。

まあそれはさておき、上の画像から高瀬七海ちゃんの顔を切り出したいなー、と思っても、顔を傾けてるので上手くいかないわけです。そこで、発想を逆転し顔が傾いてるなら、画像全体を逆に傾けてまっすぐにしちゃえばいいんじゃね?と思って以下のスクリプトを書いてみました。

# -*- coding: utf-8 -*-
#! /usr/bin/env ruby
require 'rubygems'
require 'detector'
require 'RMagick'

def create_temp_images(file)
  angles = (-30..30).step(10)
  files = angles.map{|angle| "temp#{angle}.jpg" }

  image = Magick::ImageList.new(file)
  angles.zip(files).map{|a,f| image.rotate(a).write(f) }
  files
end

def unlink_temp_images(files)
  files.map{|f| File.unlink(f) }
end

def detect_max_face(file)
  model = '/usr/local/share/opencv/haarcascades/haarcascade_frontalface_alt2.xml'
  files = create_temp_images(file)
  images = files.map{|f| Magick::ImageList.new(f) }
  faces = {}
  images.zip(files).each do |i, f|
    Detector::detect(model, f).map{|rect| faces[i.crop(*rect)] = rect[2] * rect[3]}
  end
  unlink_temp_images(files)
  max_face = faces.to_a.sort{|a, b| b[1] <=> a[1] }[0][0]
end

if __FILE__ == $0
  file = ARGV.shift
  raise "Arguments error" unless file
  raise "File not found: #{file}" unless File.exists?(file)

  max_face = detect_max_face(file)
  max_face.write('output.jpg')
end

上のスクリプトは何をやってるかというと、元画像を -30 度から 30 度まで、10 度単位で傾けた画像を 6 枚作り、これらの画像を顔検出にかけて検出できた顔の中で一番大きいサイズのものを切り出して出力する、って感じです。

で、さきほどの画像を上のスクリプトに読ませた結果↓はこんな感じ。

なかなかうまいこと切り出せました。今更ですが OpenCV おもろいですね。あ、言い忘れましたが、これらのスクリプトは昨日も書いたように detector を使っているので、ObjectDetect を使ってる人は、detector -> objectdetect、Detector::hoge -> ObjectDetect::hoge、に置換してください。まぁこんなスクリプト試したい人いないと思いますが、、、。