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カメラです
画像からの顔認識
参考サイト
処理の流れ
- 画像を取得
- 画像をモノクロに変換 (処理を軽くするため)
- カスケード型分類器を用いて、顔領域を判断する
- 顔領域に枠を書く
- 描画
という流れです。
みたいなことをやるみたいです。詳しくは、他で調べてわかりやすい記事を見て下さい><
その、学習した分類器が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
実行結果
Lennaさんの方の画像では、誤検出があるようですが、それでも結構いいかんじだと思います。画像についても、.pngと.jpgでどちらも同じコードで実行することができて、とても便利です♪
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というフィルターを利用させていただきました。
動画はアニメゆゆ式のオープニングを利用しました。(ゆいちゃんカワイイ^^)
引用元動画 : 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) > 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プログラミングにもぜひご期待ください^^
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))))
とすれば出力動画のサイズが入力に合うはずなので参考にしてください。
コメントありがとうございます。
ご指摘の通り、
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))
などを利用したほうが、汎用的で使いやすそうです。参考になりました!
ありがとうございます。
Pingback: 2015年にやりたい100の事 -達成度- | たくのこ Web
Pingback: Python + OpenCVで斜めの顔を顔認識をしてみた時の備忘録