ruby-opencvの進捗の話(2014年2月版)

はじめに

なんか気がついたら2014年も1ヶ月が過ぎてますが、ruby-opencvのお話です。 前回からバージョンもだいぶ上がり、機能も地味にいろいろ追加されてるので、ちょっとだけ紹介します。

APIリファレンス的なものを(ようやく)用意しました

前からちょっとずつYARDで書いてたアレをようやくマージしました。 rubydoc.infoで見れるのでどうぞ。

Documentation for ruby-opencv

微妙に網羅できてないのもありますが、よく使うやつはだいたい書いてあります。

FaceRecognizerを追加しました

FaceRecognizer #17 でご要望を頂いておきながらずーっと放っておいたFaceRecognizerが(v0.0.11から)使えるようになりました。 公式のチュートリアル Face Recognition with OpenCV に相当するサンプルも用意してあるので、興味のある方はぜひどうぞ。

https://github.com/ruby-opencv/ruby-opencv/tree/master/examples/facerec

FaceRecognizer(EigenFaces)のサンプルコード

せっかくなので、FaceRecognizerのEigenFacesを使って顔認識してみましょう。 Ubuntu 13.10 + Ruby 2.1.0-p0(x86_64-linux) + ruby-opencv 0.0.12 で動作確認していますが、だいたいどの環境でも同じように動くはずです。

1. 認識用の顔画像を AT&T Facedatabase から取ってくる

http://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html から認識用の顔画像を取ってきて展開します。

$ wget http://www.cl.cam.ac.uk/Research/DTG/attarchive/pub/data/att_faces.zip
$ unzip att_faces.zip

2. テストデータを用意する

学習用に顔画像をラベルと対応付けたファイルを作成します。 OpenCV公式チュートリアルこれを使ってもいいですが、 Ruby版のcreate_csv.rbも作ったので今回はこっちを使いましょう。

# create_csv.rb
if ARGV.size != 1
  puts "usage: ruby #{__FILE__} <base_path>"
  exit
end

BASE_PATH = ARGV[0]
SEPARATOR = ';'

label = 0
Dir.glob("#{BASE_PATH}/*").each { |dir|
  if FileTest::directory? dir
    Dir.glob("#{dir}/*") { |filename|
      puts "#{filename}#{SEPARATOR}#{label}"
    }
    label += 1
  end
}

画像ファイルのフォルダを引数で指定して実行します。 結果は標準出力に出るので、適当にリダイレクトしてください。

$ ruby create_csv.rb att_faces > at.txt

実行すると、下記のように 画像ファイル名;ラベル というフォーマットで対応付けのファイルが作成されます。

$ cat at.txt
att_faces/s34/2.pgm;0
att_faces/s34/3.pgm;0
att_faces/s34/8.pgm;0
att_faces/s34/4.pgm;0
att_faces/s34/5.pgm;0
att_faces/s34/10.pgm;0
att_faces/s34/9.pgm;0
att_faces/s34/7.pgm;0
att_faces/s34/6.pgm;0
att_faces/s34/1.pgm;0
...

3. 顔認識してみる

こんな感じのプログラム(eigenfaces.rb)を作ります。

# eigenfaces.rb
require 'opencv'
include OpenCV

def read_csv(filename, sepalator = ';')
  images = []
  labels = []
  open(filename, 'r') { |f|
    f.each { |line|
      path, label = line.chomp.split(sepalator)
      images << CvMat.load(path, CV_LOAD_IMAGE_GRAYSCALE)
      labels << label.to_i
    }
  }
  [images, labels]
end

if ARGV.size < 1
  puts "usage: ruby #{__FILE__} <csv.ext>"
  exit 1
end

fn_csv = ARGV.shift
output_folder = ARGV.shift

# 顔画像を読み込む
images, labels = read_csv(fn_csv);

height = images[0].rows;

# 識別対象の顔画像は学習対象とは別にしておく
test_sample = images.pop
test_label = labels.pop

# Eigenfacesのモデルを作成して学習
model = EigenFaces.new
model.train(images, labels)

# 識別処理
# 識別結果のラベルと一致度が返ってくる
predicted_label, predicted_confidence = model.predict(test_sample)

# 結果を表示
puts "Predicted class: #{predicted_label} / Actual class: #{test_label}"
puts "Confidence: #{predicted_confidence}"

w1 = GUI::Window.new('Predicted')
w2 = GUI::Window.new('Actual')

w1.show images[predicted_label]
w2.show images[test_label]

GUI::wait_key

で、実行します。引数は2で作ったファイルのファイル名です。

$ ruby eigenfaces.rb at.txt

実行結果はこんな感じ(左が識別結果で右が実際の顔)。どうやらそれっぽく動いているようです。

f:id:ser1zw:20140211002306p:plain

そんなわけで

ruby-opencvの進捗状況でした。