(第3回)Python + OpenCV で遊んでみる(顔検出編)

(第3回)Python + OpenCV で遊んでみる(顔検出編)

目次

はじめに

前回はOpenCVを利用して、どのような画像処理が出来るのかを試してみました。
今回はOpenCVに同梱されているカスケード型検出器を使って顔検出をしてみようと思います。

カスケード型検出器とは

検出器は、数千枚という画像から機械学習プログラムを実行して作成されるもので、機械学習の成果物となります。

カスケード型検出器は、多段階フィルタというタイプでいくつもの評価条件から多段階で評価し、最終的にすべての評価から検出対象だと判断する検出器です。

本来は機械学習によって、顔検出の検出器を作成しなければならないのですが、膨大な画像データを使用して、長時間の学習時間を要する必要があるため、今回はOpenCVに同梱されている検出器を使用して検出したいと思います。(今後の回で、検出器作成にも取り組んでみようと思っています。)

使用する検出器

今回はOpenCVに同梱されているHaar-likeカスケード型検出器を使用します。
同梱されている検出器は、目の検出器、鼻の検出器、顔の検出器など用意されています。

今回は正面の顔を検出したいので、haarcascade_frontalface_default.xmlを使用したいと思います。

これはHaar-like特徴という矩形領域の白色の画素の和と黒色の画素の和の差を特徴量として、検出するアルゴリズムを利用した顔検出器です。
カスケード型識別器となっているため、高速で処理が出来ます。

OpenCV – github

https://github.com/opencv/opencv

顔検出

今回使用する画像は以下の2つの画像です。

1枚目
2枚目

以下のプログラムで2つの画像から顔を検出してみます。

%matplotlib inline
import cv2
import matplotlib.pyplot as plt

# 分類器の読込
# https://github.com/opencv/opencv/tree/master/data/haarcascades
# から取得
cascade_path = "haarcascade_frontalface_default.xml"

# 画像ファイルの読込(結果表示に使用する)
img = cv2.imread('input.jpg') #カラーで読込

# グレースケールに変換(顔検出に使用する)
gry_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# カスケード検出器の特徴量を取得する
cascade = cv2.CascadeClassifier(cascade_path)

# 顔検出の実行
facerect = cascade.detectMultiScale(gry_img, scaleFactor=1.1, minNeighbors=2, minSize=(30, 30))

# 矩形線の色
rectangle_color = (0, 255, 0) #緑色

# 顔を検出した場合
if len(facerect) > 0:
    for rect in facerect:
        cv2.rectangle(img, tuple(rect[0:2]),tuple(rect[0:2] + rect[2:4]), rectangle_color, thickness=2)

img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR) #RGBからBGRに変換
plt.imshow(img)

結果は以下のようになります。

1枚目の結果
2枚目の結果

結果を見ると、1枚目の画像も2枚目の画像も顔以外の部分でも顔として検出しています。

これは、大小の矩形領域を変化させて、画像内の画素パターンを検索しており、顔以外でもそのパターンが当てはまっているからです。

また、2枚目の画像は右側の人の顔が検出されていません。
同様に検索の閾値によって顔のパターンに当てはまらなかったため検出されなかったということになります。

この閾値はdetectMultiScaleメソッドの引数で指定します。

detectMultiScaleメソッドのパラメータについて

scaleFactor :
画像スケールにおける縮小量。detectMultiScaleでは画像のスケールを何度も変化させて探索するため、その際の縮小量を設定する。大きいほど誤検知が多く、小さいほど未検出となってしまう率が高くなる

minNeighbors:
信頼性のパラメータ。検出器が検出する箇所が重複するので、より重複が多い部分が信頼性が高いこととなり、その閾値を設定します。値が大きくなるにつれて信頼性が上がるが、顔を見逃してしまう率も高くなる。

minSize :
物体が取り得る最小サイズ。これよりも小さい物体は無視される。

パラメータを変更してみると、ちゃんと顔が検出されました。

1枚目:minNeighborsを高く設定し、信頼性の高い部分だけを顔として検出するようにしました。

# 顔検出の実行
facerect = cascade.detectMultiScale(gry_img, scaleFactor=1.1, minNeighbors=6, minSize=(30, 30))
1枚目のパラメータ調整後の結果

2枚目:minSizeを大きくし、縮小量を調整することで、誤検知を減らし、顔を検出するようにしました。

# 顔検出の実行
facerect = cascade.detectMultiScale(gry_img, scaleFactor=1.05, minNeighbors=2, minSize=(100, 100))
2枚目のパラメータ調整後の結果

おわりに

今回は同梱されているカスケード型検出器を利用して、顔検出を行ってみました。

検出器を作成する(機械学習)際にもパラメータは重要だと思いますが、利用する場合にも閾値が重要であり、検出器を利用する際は検証や経験が少なからず必要だと思いました。

次回は、動画の利用と動画からリアルタイムで顔検出してみたいと思います。