Python + OpenCVで顔検出を行う


この記事のまとめ:
  • OpenCV 3をインストールする。
  • OpenCV 3に標準実装されているHaar Cascade分類器による顔検出を試す。
背景:

かなり寄り道してばっかりですが、機械学習を勉強中です。なんとなく写真から人物認識をしたくなりまして、その実現準備として写真の中から顔の部分だけを切り出すことが必要かと思い、顔検出ができるツールを探していたところ、とりあえずOpen CVが見つかったので試したいと思います。

OpenCV 3のインストール:

最新のOpenCV 3だとPython 3.6以上に完全対応していないとの情報があったので、今回はPython 3.5を前提に話をしていきます。 (参考:AnacondaディストリビューションでPythonの仮想環境を整える

私はAnaconda環境でPythonの開発をしておりますのでcondaを使って、Open CVのインストールをします。

# conda install -c https://conda.anaconda.org/menpo opencv3

なお、インストール手順についてはこちらの記事を参考にさせていただきました。

Haar Cascade分類器について

OpenCVに実装されている顔検出は、Haar Cascade分類器というものを使って人の顔かどうか判断します。詳細の理解まではしていませんが、幾つかの単純な特徴を捉える分類器によって構成されるためカスケード分類器と呼ばれるようです。その単純な特徴を捉える分類器をどのように組み合わせて構成するかで写真の中のオブジェクトを検出させるもののようです。OpenCVには予めいくつかのオブジェクトを検出するための構成情報がXMLファイルとして用意されております。その中で人間の正面の顔を検出するXMLファイルは下記の4つがあります。

  • haarcascade_frontalface_default.xml
  • haarcascade_frontalface_alt.xml
  • haarcascade_frontalface_alt2.xml
  • haarcascade_frontalface_alt_tree.xml

これらのファイルは、OpenCVをインストールしている方は、PythonのインストールディレクトリからLibrary\etc\haarcascadesで見つかられるのではないかと思います。

文献を詳しく読んでいないため、これらのファイルがどのような違いがあるのか、どのように学習させたものなのか全く分かっておりませんが、とりあえず試してみてから考えたいと思います。

OpenCVのHaar Cascade分類器を使ってみる

上記の4つのXMLファイルで写真の中の顔検出を試すサンプルコードを以下に示します。

import sys
import cv2
 
def face_ditector(image_file, xml_file):
    faceCascade = cv2.CascadeClassifier(xml_file) # XMLファイルを読み込む
 
    img = cv2.imread(image_file, cv2.IMREAD_COLOR) # 画像を読み込む
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # グレースケールに変換する
    face = faceCascade.detectMultiScale(gray, 1.03, 5) # Cascade分類器を使って顔認識を行う
 
    if len(face) > 0:
        for rect in face:
            cv2.rectangle(img, tuple(rect[0:2]), tuple(rect[0:2]+rect[2:4]), (0, 0,255), thickness=2) # 画像上に顔と検出された領域を描く
    else:
        print("no face")
 
    cv2.imwrite('{0}_{1}'.format(filter, image_file), img) # 画像を出力する
 
def main(argv):
    image_file  = argv[1]
    xml_list = ["haarcascade_frontalface_default.xml","haarcascade_frontalface_alt.xml","haarcascade_frontalface_alt_tree.xml","haarcascade_frontalface_alt2.xml"]
 
    for xml_file in xml_list:
        face_ditector(image_file, xml_file)
 
if __name__ == '__main__':
    main(sys.argv)

Cascade分類器を実行する際に、パラメーターが3つあります。 上記のサンプルコードでは(gray, 1.03, 5)となっているところです。 それぞれ次のようなパラメーターを入力する必要がります。

  1. image: CV_8U型、つまりグレースケールの行列で表される画像ファイル
  2. scaleFactor: (詳しい説明があまりなかったので私の認識です)様々なサイズのオブジェクトを検出するために元の画像情報をスケールさせて分類器にかける際にどの程度の係数でスケールさせるかのパラメーターです。設定範囲は1.01以上で0.01 (=1%)単位で変更でき、値が小さいほど細かいスケーリングの粒度で検索できますが、その分時間がかかります。
  3. minNeighbors: 画像検出する手段として画像内の検索対象とする矩形を少しずつずらしてその矩形内の画像情報をCascade分類器に通して検出をさせているようなのですが、若干のズレを伴って複数の位置で画像を検出できるかどうかのパラメーターです。値が大きいほどズレを伴っても検出できるということなので検出精度は上がりますが、大きすぎると逆に下る可能性があります。値が小さすぎると検出誤りが発生しやすくなります。
  4. flags/ minSize/ mazSize(省略可能): 省略可能パラメーターなので説明も省略します。

説明を読んでいただければある程度精度が高くなるようにパラメーターを設定したつもりです。

なお、サンプルコードは下記の記事を参考にさせていただきました。

結果

今回は6枚の写真を使って評価してみました。

1枚目
defalt: 検出精度が悪く、余計なものまで検出していますね。

alt: defaultより改善。

alt2: altとは違うところを誤って検出。

alt_tree: 誤りはなくなりましたが人も検出されなくなりました。

2枚目
default: 先程と同様に余計なものまで検出しています。

alt: 顔のみをうまく検出できています。

alt2: 一箇所だけ余計なものを検出しています。

alt_tree: 先程の写真と同様に顔のみを検出できています。

3枚目
default: 黒人の方が顔の上半分のみを検出するというわけのわからないことに。

alt: 誤ってはいませんが検出数が少ないです。

alt2: まあまあいい感じですが正面の顔用のXMLファイルなので横顔は検出できませんね。

alt_tree: altと同様に誤ってはいませんが判定基準は厳しめな感じですね。

4枚目
default: ネクタイとか、手とかを検出しまっていますのでやはり判定基準は緩めですね。

alt: おしい!

alt2: やはりaltよりも誤りが多いですね。

alt_tree: 顔だけしか検出していませんがやはり検出基準が厳しいようです。

5枚目
default: もはや顔以外しか検出していません。

alt: 横顔は検出できませんね。

alt2: 同上、横顔は検出できませんね。

alt_tree: もちろん検出できませんね。

6枚目
default: そこまで悪くはないですが誤りはあります。

alt: うまく顔だけ検出しています。

alt2: やはりaltより誤りは多そうです。

alt_tree: これまでと同様誤りはありませんが、検出数が少ないです。

最後に

結果的には以下のような感じでしょうか。

  • 顔検出精度(顔以外を検出しない): alt_tree > alt > alt2 > default
  • 顔検出能力(多くの顔を検出する): default > alt2 > alt > alt_tree

一長一短の結果と、横顔になると検出できないのはどれも違いないのでいまいちの結果でした。

別のアルゴリズムを試してみようと思って、Dlibを使ってHOGアルゴリズムによる顔検出を試してみたところ、断然精度が高かったのでもしご興味あれば下記の記事に使い方や差分がわかる結果を載せていますのでご覧ください。




今回は以上です。 最後まで読んでいただき、ありがとうございます。


コメント

このブログの人気の投稿

PythonでPosgreSQLを操作する

Python + dlibで顔検出を行う