私は今、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でアニメ 「けいおん!」を画像認識させてみた)を参考にしました。今回のコードだけではなく、こちらの記事を参考に進めています。
[…] 関連記事:OpenCVのカスケード分類器で顔検出をしてみた! […]
[…] して、顔部分だけの画像を生成します。また、64 x 64にリサイズします。その方法についてはこちらの記事(関連記事:OpenCVのカスケード分類器で顔検出をしてみた!)にまとめました。 […]