Pythonで遊んでみる -part1- (OpenCVで顔認識)

Python+OpenCVで遊んでみるpart1ってことで、顔認識をやってみました。
さらに、顔認識を利用してアニメOPでの顔認識を行ってみた動画を作ってみました〜

 

環境

以前の記事で作成した通りの環境です。

  • MacBookAir 2013
  • Intel core i5 1.3GHz
  • python2.X (anaconda-2.1.0)
  • openCV 2.4.10
  • Webカメラ(ELECOM製 :UCAM-DLD200BABK)
    1080p/30fpsができる、普通のWebカメラです

 

画像からの顔認識

参考サイト

WEBカメラ映像から顔検出 | 画像処理速報WEBカメラ映像から顔検出 | 画像処理速報

処理の流れ

  1. 画像を取得
  2. 画像をモノクロに変換 (処理を軽くするため)
  3. カスケード型分類器を用いて、顔領域を判断する
  4. 顔領域に枠を書く
  5. 描画

という流れです。

 

memo : カスケード型分類器とは?
公式サイトによると、正しい画像と間違った画像から学習し分類器(特徴量?)を作る->その分類器をもとにサイズ変更だの回転だのしながらいろいろな場所で一致しないか探す。
みたいなことをやるみたいです。詳しくは、他で調べてわかりやすい記事を見て下さい><
カスケード型分類器 | opencv.jp カスケード型分類器

その、学習した分類器がOpenCVには標準で入っています。すばらしい!場所は[opencv > sources > data > haarcascades]とありますが、自分はpyenvでホームディレクトリにインストールしていて、[~/.pyenv/versions/anaconda-2.1.0/share/OpenCV/haarcascades/]にありました。

 


# -*- coding: utf-8 -*-
# 画像からカスケード分類器を用いて顔認識を行うサンプル

import cv2

# サンプル顔認識特徴量ファイル
cascade_path = "../lib/haarcascades/haarcascade_frontalface_alt.xml"
image_path = "img/Lenna.png"

# これは、BGRの順になっている気がする
color = (255, 255, 255) #白

# 画像の読み込み
image = cv2.imread(image_path)
# グレースケール変換
gray = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY)

# 分類器を作る?みたいな作業
cascade = cv2.CascadeClassifier(cascade_path)

# 顔認識の実行
facerect = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=1, minSize=(1, 1))

if len(facerect) > 0:
  # 検出した顔を囲む矩形の作成
  for rect in facerect:
    cv2.rectangle(image, tuple(rect[0:2]),tuple(rect[0:2]+rect[2:4]), color, thickness=2)
else:
  print("no face")

# 認識結果の表示
cv2.imshow("detected.jpg", image)

# 何かキーが押されたら終了
while(1):
  if cv2.waitKey(10) > 0:
    break

実行結果

オバマ大統領

オバマ大統領

(Wikipediaより引用)

Lennaさんの方の画像では、誤検出があるようですが、それでも結構いいかんじだと思います。画像についても、.pngと.jpgでどちらも同じコードで実行することができて、とても便利です♪

 

Webカメラからの映像取得

顔認識の次は、Webカメラの映像を取得してみました。

参考ページ

WEBカメラの映像をキャプチャ | 画像処理速報WEBカメラの映像をキャプチャ | 画像処理速報

ほぼ、参考サイト様のソースコードのままです。。。仕様としては、Spaceキーで画像を保存、Escキーでプログラムを終了という感じです。


# -*- coding: utf-8 -*-
import cv2
import time

cap = cv2.VideoCapture(0) # カメラセット?
i = 0
while(1):
 start = time.clock() # 開始時刻
 ret, image = cap.read() # 画像を取得する作業
 get_image_time = int((time.clock()-start)*1000) # 処理時間計測
 # 1フレーム取得するのにかかった時間を表示
 cv2.putText( image, str(get_image_time)+"ms", (20,20), 1, 1, (0,255,0))
 cv2.imshow("Camera Test",image)
 # キーが押されたら終了
 if cv2.waitKey(10) == 32: # 32:[Space]
 cv2.imwrite(str(i)+".jpg",image)
 i+=1
 print("Save image..."+str(i)+".jpg")
 elif cv2.waitKey(10) > 27: # 27:Esc
 cap.release()
 cv2.destroyAllWindows()
 break

こちらのプログラムに顔認識を埋め込んだのですが、解像度の割にスペック不足だったため、動作が不安定な感じでした。解像度を下げたりすれば、できたのかもしれません。

 

動画での顔認識

今度は、この顔認識を用いてアニメの動画に現れる顔を検出してみたいと思います。
顔の検出には、nagadomi様の作成したlbpcascade_animeface.xmlというフィルターを利用させていただきました。

nagadomi/lbcascade_animefacenagadomi/lbpcascade_animeface | Github

動画はアニメゆゆ式のオープニングを利用しました。(ゆいちゃんカワイイ^^)

引用元動画 : https://www.youtube.com/watch?v=Wx_g2jN0rY0

検出精度としては、誤検出もそれなりにあるとはいえ、かなり高い精度での検出ができております。すばらしい!!!

 

ソースコード

# -*- coding: utf-8 -*-

# 動画ファイルから、顔認識したものを取り出す

# 分類器をアニメ向けのもの使用
import cv2
import time

# 分類器へのパス
cascade_path = "../lib/lbpcascade_animeface/lbpcascade_animeface.xml"

# 動画パス
video_path = ""../img/yuyusiki_op.mp4""
out_video_path = ""../yuyu/face_yuyusiki_op.avi""

# colorはBGRの順番?
color = (0, 187, 254) #黄
#カスケード分類器の特徴量を取得する
cascade = cv2.CascadeClassifier(cascade_path)

# 動画のエンコード とりあえず、これで動く拡張子はm4vで
fourcc = cv2.cv.CV_FOURCC('m', 'p', '4', 'v')
# 動画ファイル読み込み
cap = cv2.VideoCapture(video_path)

out = cv2.VideoWriter("face_output.m4v", fourcc, 30.0, (1280,720))

frame_num = 0
img_cnt = 0
# フレームごとの処理
while(cap.isOpened()):
  ret, frame = cap.read()
  if (ret == False):
    break
  frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  facerect = cascade.detectMultiScale(frame_gray, scaleFactor=1.1, minNeighb  ors=1, minSize=(1, 1))

  print("frame : %d" % frame_num)
  if len(facerect) &gt; 0:
    #検出した顔を囲む矩形の作成
    for (x,y,w,h) in facerect:
    cv2.rectangle(frame, (x,y),(x+w,y+h), color, thickness=7)
    img_cnt += 1
  out.write(frame)
  frame_num += 1

cap.release()
cv2.destroyAllWindows()
out.release()

やっていることは、先ほどの顔検出のプログラムとほとんど同じでフレームごとに顔を検出してそれらを動画として出力しているだけです。作成される動画には音声が含まれていないので後からiMoveを利用して合成しました。

ちなみに、この動画は90秒程で720p,30fpsで処理に約20分ほどかかりました…。

 

ちょっとはまった所
動画のエンコード

出力させる動画の設定でエンコードがいろいろ選べるのですが、なかなかうまく行かずに苦労しました…。 再生環境が悪いのか、pythonで動画作成に必要な何かが足りなかったのか…

最終的に以下の設定でうまくいきました。

# ソースコード一部抜粋
out_video_path = "../output/output.m4v"
fourcc = cv2.cv.CV_FOURCC('m', 'p', '4', 'v')
out = cv2.VideoWriter(out_video_path, fourcc, 30.0, (640,360))

エンコードをCV_FOURCC(‘m’, ‘p’, ‘4’, ‘v’)にして、出力を.m4vにしました。原因不明ですけど、とりあえずめでたし…。

 

動画のサイズ

動画のサイズを入力と出力で合わせないと、エラーがでます。

例えば、入力画像が1280*720の場合に、下記のようなコードを書くとエラーです。

# out = cv2.VideoWriter(out_video_path, fourcc, 30.0, (1280,720))
out = cv2.VideoWriter(out_video_path, fourcc, 30.0, (640,360))
# frameは1280*720
out.write(frame)
# 普通に書き込もうとするとエラー
OpenCV Error: Assertion failed (dst.data == dst0.data) in cvCvtColor, file /Users/travis/miniconda/conda-bld/work/opencv-2.4.10/modules/imgproc/src/color.cpp, line 4424
Traceback (most recent call last):
 File "get_face_out_video.py", line 48, in <module>
 out.write(frame)
cv2.error: /Users/travis/miniconda/conda-bld/work/opencv-2.4.10/modules/imgproc/src/color.cpp:4424: error: (-215) dst.data == dst0.data in function cvCvtColor

この場合は、動画を変換してからなら書き込む事ができます。

# out = cv2.VideoWriter(out_video_path, fourcc, 30.0, (1280,720))
out = cv2.VideoWriter(out_video_path, fourcc, 30.0, (640,360))
# frameは1280*720
out.write(cv2.resize( frame, (640,360))
# これなら問題ない

 

まとめ

画像を読み込んで、そこから顔の部分に枠をつける。これがとても簡単に実装できました。
1日でここまでできるなんて、さすがPython。楽しい♪

これからのPythonプログラミングにもぜひご期待ください^^

Pocket
Bookmark this on Google Bookmarks

4 thoughts on “Pythonで遊んでみる -part1- (OpenCVで顔認識)

  1. kivantium


    out = cv2.VideoWriter(out_video_path, fourcc,
    int(cap.get(cv2.cv.CV_CAP_PROP_FPS)),
    (int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH)),
    int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))))

    とすれば出力動画のサイズが入力に合うはずなので参考にしてください。

    1. takunoko Post author

      コメントありがとうございます。

      ご指摘の通り、
      int(cap.get(cv2.cv.CV_CAP_PROP_FPS))
      int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_WIDTH))
      int(cap.get(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT))
      などを利用したほうが、汎用的で使いやすそうです。参考になりました!
      ありがとうございます。

  2. Pingback: 2015年にやりたい100の事 -達成度- | たくのこ Web

  3. Pingback: Python + OpenCVで斜めの顔を顔認識をしてみた時の備忘録

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です