さて、このシリーズも本編に関してはこれで終わりです。
正直、前回までで機能は全部実装してしまったので今回もおまけみたいなものに見えますけど...
今回は前回まで使用していたlbpcascade_animefaceではなく、僕が学習させたモデルを使用してみます。
では、いつものスクリプト配布です。
GitHub Gist - TL_capture_dlib.py
detector_face.svm - marron-akanishi/TPTS_web(直リン、右クリックで保存を推奨)
detector_eye.svm - marron-akanishi/TPTS_web(直リン、右クリックで保存を推奨)
Gistの中には2つファイルがあります。
毎回のことですが、dlib_detector.pyのファイル名は変えないでください。参照できなくなります。
今回はモデルが変わるので、そのモデルも配布しています。2つありますが、どちらもダウンロードしてください。
では、実行してみます。最後ですし、もう説明要らないよね?
実行結果も何も変わりません。
ファイル数が増えたので、少し整理した状態のフォルダー構造にしてあります。
では、メインスクリプトの変更点です。
左が前回、右が今回のスクリプトとなっています。
なんと変更点が1つしかありません。 検出部分を変えただけなので、こっちの変更がほとんど無いのです。
変更した部分はimport文だけです(10行目)。ここも関数名はそのままでファイル名を変えただけとなっています。
ということで肝心の検出用スクリプトをさっそく見ていきます。
import numpy as np import cv2 from dlib import simple_object_detector face_detector = simple_object_detector("./detector_face.svm") eye_detector = simple_object_detector("./detector_eye.svm") def face2d_detect(raw_file): # 取得するか is_get = False # 顔の位置 facex = [] facey = [] facew = [] faceh = [] # 画像をデコード image = cv2.imdecode(np.asarray(bytearray(raw_file), dtype=np.uint8), 1) # 画像から顔を検出 try: faces = face_detector(image) except: faces = 0 # 顔が検出出来たか if len(faces) > 0: # 顔だけ切り出して目の検索 for i, area in enumerate(faces): face = image[area.top():area.bottom(), area.left():area.right()] # 出来た画像から目を検出 eyes = eye_detector(face) if len(eyes) > 0: facex.append(area.left()) facey.append(area.top()) facew.append(area.right()-area.left()) faceh.append(area.bottom()-area.top()) is_get = True return is_get, [facex, facey, facew, faceh]
いつものようにコードを埋め込んでみました。では、見ていきます。
ちなみにlbpcascade.pyと変更がない部分は飛ばしていきます。
説明内での行番号はファイル内での行番号となっているので、上のスクリプトで見る際は-2した行番号でみてください。
まず、今回はdlibと呼ばれるライブラリを利用して検出を行うため、import文が増えています(5行目)。
次にモデルのロードです(7~8行目)。
前回までは1つしかファイルが無かったので、1行で済んでいましたが今回は2つ使用するため、2つ検出器を作成しています。なぜ2つあるのかは後から説明します。
次に取得するかのフラグを別の変数として用意しました(12行目)。
前回までreturnに直書きしていたフラグを変数に分けただけです。
次に画像の読み込みが少し変化しています(20行目)。
今まではグレースケールに変換していましたが、今回は変換していません。変化が特に見られなかったので、変換するのを止めただけです。別に前回同様にグレースケールに変換しても問題ありません。
次に顔検出を行います(22~25行目)。
ここでエラー処理を行っています。これはたまに画像の形式が対応していなくてエラーを吐くことがあったため、実装しました。face_detectorの返却値は前と同様に座標情報群です。
次に顔が検出できたかを確認しています(27行目)。これも前回までと変わりません。
次に画像から顔だけを切り出して、目を検出しています(29~32行目)。ここが前回までと大きく異なる場所です。
前回までは顔が検出できたら、そのまま座標情報をリストに突っ込んでいました。
ですが、その結果顔では無いところまでリストに入ってしまいました。
そこで今回は顔として検出できたなら目も存在するだろうということで、目の検出を行っています。これにより、精度が改善されました。ただ、逆に言えば判定が厳しくなったので取りこぼす確率も上がりました。どちらがいいかはまあ人それぞれって感じですかね。
ちなみに顔のみで行うと精度が結構下がります。学習が悪かっただけなのですが...
目が検出できたら、そのデータをリストに登録してフラグをTrueにします(33~38行目)。
前回まではただのリストだったので座標を数字で指定していましたが、今回はメソッドで取得しています。こっちのほうが読みやすいですね。
最後にフラグとリストを返却しています(39行目)。特に変更はありません。
顔検出部分の変更点はこんな感じです。
では、理論的な部分の説明にいきましょう。いつものように参考URLをいくつか貼っていきます。
前回まではOpenCVに入っている検出器を使用していましたが、今回は機械学習用ライブラリであるDlibの検出器を利用してみました。特にDlibじゃないといけないというこだわりは無いのですが、検索していると見つかったので使ってみたという感じです。
一番最初に見つけたサイトです。特に学習部分はお世話になりました。
こっちも読みました。
今回のモデルはHOG特徴とSVM(サポートベクターマシン)というものを使用しています。
どっちも載ってるありがたいページを見つけました。説明は全部丸投げします(おい
読めば分かりますが、実は検出は前回と同様にグレースケールで行われています。なら、グレースケールにしたほうが速いのでは...
ちょっとここで検出結果を見てみましょう。形式は前から変更していないので、今回のDBもTPTSViewerで開くことができます。
さて、少し精度が下がっていますね。ですが、まあ画像としては収集できているので今回はよしとしましょう。
で、枠のサイズを見ていただきたいのですが顔ギリギリを取ってます。
これは僕が学習させたときにギリギリで囲っていったからです。こっちのほうがいいと思いませんか?
今回はこれぐらいで終わります。本当は学習まで書こうと思ったのですが、別の記事に分けることにしました。なんかね、今日疲れてるんですよ。
学習に関しては、少し環境の整備が必要なんですよね。特に今回配っているPythonが32bit版なのですが、学習には64bit版が必要になります。メモリをそれだけ使用するってことです。
機械学習というとどうしても最近だとディープラーニングばかりが注目されていますが、こんな感じの昔ながらの方法でも結構面白いことが出来ます。
ただ、ディープラーニングを利用したほうがもっと出来ることが増えるのは確かです。
ちなみに今回学習させたデータは僕はTPTSを作ってすぐに作成したものです。
それ以降もたまに作っていたのですが、これ以上精度が良いものが出来ないというのが現状です。というのも、これ以上データを増やして学習すると逆に過学習に陥って、精度が下がるんですよね。やっぱり方式変えるしか無いようです。
ただ、僕GPUがRX460なんで学習に使えないんですよね。誰かGTX 1060をください。
今回は二次元画像の収集という名目でやりましたが、これをするなら一番ベストなのは写真とイラストを判別することだとずっと思っています。
ただ、いい方法が思いつきません。いいアイデアが欲しいですね。
さて、これで皆さんもTPTSを作ることが出来ました(単にスクリプトを配布していただけ?)。
バックがこれで出来ているTPTS_webでは、TL以外の場所からも画像を収集出来るので使ってみてください。Twitterアカウントでログインするだけで使用できます。
次回は番外編の予定ですが、いつになるか分かりません。
それではお疲れ様でした。