OpenCVのカスケード分類器で顔検出をしてみた!

私は今、Aidemy Premium Planという、未経験者が3か月で機械学習、ディープラーニング、データ分析、AIアプリ開発まで最先端技術を幅広く学べるオンライン学習サービスで勉強しています。

そして、AIアプリ開発コースにおいて、AIアプリの自主制作に取り組んでいます。

私が製作しているアプリは、『嵐のメンバーでいうと誰?』というアプリです。顔画像をアップロードすると、嵐のメンバーで最も似ているメンバーを教えてくれるというアプリです。製作の大きな流れはこんな感じ。

  • 学習用の画像の収集
  • 嵐メンバーの学習
  • アプリに実装

学習用の画像の収集については、Bing Image Search APIを使いました。こちらの記事(【Bing Image Search API v7】学習用の画像を収集してみた!)にまとめております。

学習用の画像を収集しましたが、これをそのまま学習に使うべきではありません。なぜなら、収集した画像には顔部分以外も含んでいるので、純粋に顔の学習ができないからです。

そこで、顔部分の画像にしてやる必要があります。例えば、このように全身の画像を、顔部分の画像にする処理を行います。

今回は、OpenCVというライブラリを使って顔検出をして、顔部分だけの画像を生成します。OpenCVには顔や目を検出できるカスケード分類器の学習済みファイルがあるので、それを使います。

OpenCVのカスケード分類器の使い方

Github(https://github.com/opencv/opencv)から、Clone or downloadをクリックして、Download zipをクリックしてダウンロードします。そして、opencv-master/data/haarcascades/の中にあるhaarcascade_frontalface_default.xmlというファイルを使います。

まず、こちらの画像を顔検出して四角で囲んでみます。

face.pyにコードを書きます。face.pyと同じ階層にhaarcascade_frontalface_alt.xmlファイルを置いときます。face.pyのコードは以下。

参考にした記事はこちら(PythonでOpenCVを使った顔検出してみた)。

import cv2
import matplotlib.pyplot as plt

# 画像ファイルを読み込む
img = cv2.imread("image.jpg")
# 画像をグレースケールへ変換
img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

# カスケードファイルのパス
cascade_path = "haarcascade_frontalface_alt.xml"
# カスケード分類器の特徴量取得
cascade = cv2.CascadeClassifier(cascade_path)
# 顔認識
faces=cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=1, minSize=(10,10))

# 検出位置描画
for x,y,w,h in faces:
    cv2.rectangle(img, (x,y), (x+w, y+h), (0, 0, 255), thickness=30)

# 顔検出画像表示
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.show()
# 顔検出画像出力
cv2.imwrite("out.jpg", img)

顔認識をする場合は、グレースケールへ変換するのが一般的のようです。

顔認識cascade.detectMultiScaleでは、パラメータ(scaleFactor, minNeighbors, minSize)を設定できます。パラメータについてはこちらの記事(物体検出(detectMultiScale)をパラメータを変えて試してみる(scaleFactor編))を参考にしてください。

結果はこちら。見事に顔が検出されています。

顔検出して画像を保存

上記のOpenCVのカスケード分類器を使って、嵐のメンバー(ここでは二宮和也)の顔検出をして画像を保存します。

Desktopにあるarashiディレクトリの中にface_detect.py、haarcascade_frontalface_alt.xml、二宮君の画像が入ったninomiyaフォルダを置きます。

  • arashiの構造
    • face_detect.py
    • haarcascade_frontalface_alt.xml
    • ninomiya
      • 二宮画像1
      • 二宮画像2
      • 二宮画像3
      • ・・・

こちらがコード(face_detect.py)です。

import cv2
import glob
import os

# メンバー名
member_name = "ninomiya"

# 嵐メンバーの画像フォルダのパス
path ="/Users/ユーザー名/Desktop/arashi/" + member_name
# 嵐メンバーの画像フォルダの中の全画像のパスを取得して配列化
img_path_list=glob.glob(path + "/*")

# 番号の初期化
count = 0

# 画像パス配列から画像パスを取り出していくループ
for img_path in img_path_list:

    count += 1
    print(str(count) + "/" + str(len(img_path_list)))

    # 画像ファイル名を取得
    base_name = os.path.basename(img_path)
    # 画像ファイル名nameと拡張子extを取得
    name,ext = os.path.splitext(base_name)

    # 画像ファイル以外のファイルの場合はループをスキップ
    if (ext != '.jpg') and (ext != '.jpeg') and (ext != '.png'):
        print("not a picture")
        continue

    # 画像ファイルを読み込む
    img_src = cv2.imread(img_path, 1)
    # 画像をグレースケールへ変換
    img_gray = cv2.cvtColor(img_src, cv2.COLOR_RGB2GRAY)

    # カスケードファイルのパス
    cascade_path = "haarcascade_frontalface_alt.xml"
    # カスケード分類器の特徴量取得
    cascade = cv2.CascadeClassifier(cascade_path)
    # 顔認識
    faces=cascade.detectMultiScale(img_gray, scaleFactor=1.1, minNeighbors=1, minSize=(10,10))

    # 顔がない場合はループをスキップ
    if len(faces) == 0:
        # print("no face" + name + ext)
        number_no_face += 1

        # --- 以下部分はコメントアウトしてもOK ---------------
        # 顔を検出出来なかった場合は、その画像を保存する
        # ディレクトリ名指定
        dirname = member_name + "_no_face" 
        # ディレクトリがない場合は作成
        if not os.path.exists(dirname):
            os.mkdir(dirname)
        # ファイル名指定(元の画像のファイル名base_nameを使う)
        file_name = dirname + "_" + base_name + ext
        # ディレクトリ名とファイル名を結合
        file_path = os.path.join(dirname, file_name)
        # ファイルの保存
        cv2.imwrite(file_path, img_src)
        # --- 以上部分はコメントアウトしてもOK ---------------
        
        # スキップ
        continue
    

    # 顔がある場合
    number_face += 1
    # 顔部分画像を取得
    for x,y,w,h in faces:
        face = img_src[y:y+h, x:x+w]

    # リサイズ
    face = cv2.resize(face, (64, 64))

    # 顔を検出できた画像を保存する
    # ディレクトリ名指定
    dirname = member_name + "_face" 
    # ディレクトリがない場合は作成
    if not os.path.exists(dirname):
        os.mkdir(dirname)
    
    # ファイル名指定
    file_name = dirname + "_" + str(number_face) + "_" + name + ext
    file_name = dirname + "_" + str(number_face) + "_" + ext
    # ディレクトリ名とファイル名を結合
    file_path = os.path.join(dirname, file_name)

    # ファイルの保存
    cv2.imwrite(file_path, face)

このように、二宮君の顔検出した画像が保存されます。

しかしながら、すべての画像が顔検出できるわけではなく、顔検出ができなかった画像(以下)もありました。顔が横向きだったり、顔が斜めになっている場合は顔検出が難しいようです。

と〜げ

今回、コードはこちらの記事(Kerasでアニメ 「けいおん!」を画像認識させてみた)を参考にしました。今回のコードだけではなく、こちらの記事を参考に進めています。

2 COMMENTS

【Bing Image Search API v7】学習用の画像を収集してみた! | と〜げのブログ へ返信する コメントをキャンセル

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