1.あらすじ
今後の顔分類の実験の準備として、OpenCVのカスケード分類器を用いてゲーム実況系YoutubeグループSKJvillageのスクリーンショットから顔のみを切り抜く処理を行いました。複数の分類器で顔を切り抜きを行いましたが、本件のデータに対しては分類器ごとに向き不向きがあると感じたので、今回は実際に切り抜いた画像に注目し性能評価を行いました。
2.改めてSKJvillageとは
3人組のゲーム実況系YouTubeチャンネル。高校時代の親友で「早稲田・慶應・上智」大学にそれぞれ進学し、大手企業を退職後、2018年に3人でYoutube活動開始。
(https://www.youtube.com/@SKJVillage/about より参考)
3.顔を切り抜くためのプログラム
今回は、Google Colaboratory上でpythonとOpenCVを用いて、SKJvillageのスクリーンショットから顔を探し、顔だけを切り抜いた画像を生成しました。
!git clone https://github.com/opencv/opencv.git
import matplotlib.pyplot as pyplt
import cv2 as cv
import os
import glob
#画像から顔を切り取りfaceのディレクトリに保存
names = ["saikatsu","sa2","shinka","sai×sa","sai×shin","sa×shin","SKJ","others"]
img_path = "/content/drive/My Drive/Colab Notebooks/SKJvillage_dataset/"
face_path = "/content/drive/My Drive/Colab Notebooks/SKJvillage_dataset/face/"
for i in range(len(names)):
#元画像の顔部分を囲み、64×64にリサイズし、ファイル名ごとにフォルダに出力
in_dir = img_path + names[i] + "/*.jpg"
in_jpg = glob.glob(in_dir)
os.makedirs(face_path + names[i], exist_ok=True)
print(names[i])
print(len(in_jpg))
for num in range(len(in_jpg)):
image=cv.imread(str(in_jpg[num]),cv.IMREAD_UNCHANGED)
if image is None:
print("Not open:",num)
continue
img_gs = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
#分類器の選択(今回はaltかalt2かdefault)
cascade = cv.CascadeClassifier("/content/opencv/data/haarcascades/haarcascade_frontalface_alt.xml")
#cascade = cv.CascadeClassifier("/content/opencv/data/haarcascades/haarcascade_frontalface_alt2.xml")
#cascade = cv.CascadeClassifier("/content/opencv/data/haarcascades/haarcascade_frontalface_default.xml")
# 顔認識の実行
face_list=cascade.detectMultiScale(img_gs, scaleFactor=1.1, minNeighbors=2,minSize=(64,64))
#顔が1つ以上認識できた時
if len(face_list) > 0:
for rect in face_list:
x,y,width,height=rect
image = image[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]
if image.shape[0]<64:
continue
image = cv.resize(image,(64,64))
#保存
fileName=os.path.join(face_path+"/"+names[i],str(num)+".jpg")
cv.imwrite(str(fileName),image)
print(str(num)+".jpgを保存!.")
#顔が認識できなかった時
else:
print("no face")
continue
print(image.shape)
OpenCVのダウンロード
OpenCVには、顔認識を行う分類器が数々設定されています。最初に下の1文を実行することで、github上の顔認識に関する分類器の特徴量を扱えます。
!git clone https://github.com/opencv/opencv.git
分類器の取得
分類器の選択を行い、読み込みと取得をすることで既存の分類器を扱うことができる。今回は、正面を向いている顔を検出できる分類器の中で、前回の実験で性能が良かった以下の3つの分類器で実験を行います。
・haarcascade_frontalface_alt.xml
・haarcascade_frontalface_alt2.xml
・haarcascade_frontalface_default.xml
↓前回の記事
#分類器の選択(今回はaltかalt2かdefault)
cascade = cv.CascadeClassifier("/content/opencv/data/haarcascades/haarcascade_frontalface_alt.xml")
#cascade = cv.CascadeClassifier("/content/opencv/data/haarcascades/haarcascade_frontalface_alt2.xml")
#cascade = cv.CascadeClassifier("/content/opencv/data/haarcascades/haarcascade_frontalface_default.xml")
# 顔認識の実行
face_list=cascade.detectMultiScale(img_gs, scaleFactor=1.1, minNeighbors=2,minSize=(64,64))
画像の切り抜き
選択した分類器で顔認識後に、認識した箇所を64×64にリサイズして、faceというフォルダにどんどん出力していきます。出力する際には、カテゴリ(saikatsu,sa2,shinkaなど)ごとにフォルダを生成し、jpegファイルで出力します。
#顔が1つ以上認識できた時
if len(face_list) > 0:
for rect in face_list:
x,y,width,height=rect
image = image[rect[1]:rect[1]+rect[3],rect[0]:rect[0]+rect[2]]
if image.shape[0]<64:
continue
image = cv.resize(image,(64,64))
#保存
fileName=os.path.join(face_path+"/"+names[i],str(num)+".jpg")
cv.imwrite(str(fileName),image)
print(str(num)+".jpgを保存!.")
#顔が認識できなかった時
else:
print("no face")
continue
print(image.shape)
4.顔を切り抜いた結果
以下の画像のように、顔部分が切り抜かれた64×64の画像を切り抜くことができました。ちゃんとメンバーの顔を切り抜けている場合もあれば、謎の画像が生成されている場合もありますね。(リトルマックイケメンすぎ問題)
出力は以下のように、作成したデータセットのカテゴリごとに出力されます。データセット、カテゴリについては、画像の下の記事から!
5.本件の各分類器の評価
今回は、3つの分類器でそれぞれ実行し、顔の切り抜きを行いました。扱う分類器ごとに結果が異なっていたので、せっかくなら評価してみようかなと思いました。どれだけ顔を認識できているかを数値で評価します。
※ここでの顔は、SKJvillageのメンバー以外の顔も含めます。
どれだけ顔を認識できているか(数値で評価)
出力した枚数だけで評価するのではなく、「何枚出力していてそのうち何枚が顔であるか」という割合で評価を行います。割合で評価する理由としては、「出力した画像は多いけどそれらが顔ではない」「出力した画像は少ないけど一応全て顔である」といった場合が存在したからです。下の表に結果をまとめます。
表記として、「x(y/z)」 (x:顔の割合,y:顔の枚数,z:出力した画像の枚数)と表記しています。例えば、「0.5000(25/50)」と書いてある場合は、「出力した画像50枚のうち顔だったのは25枚で割合は0.5000」という意味です。割合の有効数字は4桁とします。
saikatsu | sa2 | shinka | sai×sa | sai×shin | sa×shin | SKJ | others | 全体 | |
---|---|---|---|---|---|---|---|---|---|
alt | 0.8873(63/71) | 0.7222(52/63) | 0.7241(21/29) | 0.8750(14/16) | 1.000(3/3) | 0.7000(14/20) | 0.8304(93/112) | 0.5000(4/8) | 0.7952(264/332) |
alt2 | 0.8857(62/70) | 0.7361(53/72) | 0.6897(20/29) | 0.7895(15/19) | 1.000(4/4) | 0.6000(12/20) | 0.7674(99/129) | 0.5000(4/8) | 0.7664(269/351) |
default | 0.7692(60/78) | 0.4943(43/87) | 0.5116(22/43) | 0.5909(13/22) | 0.5000(2/4) | 0.4583(11/24) | 0.6015(80/133) | 0.6000(6/10) | 0.5910(237/401) |
結果を見ると、切り抜くことのできた顔の全体の枚数はalt2が269枚と多いですが、割合だとaltの方が0.7952と大きいようでした。カテゴリごとに見てもaltの方が割合が大きい場合が多く、割合で見るとaltの性能が高いという結果になりました。 defaultは多くの画像の出力していますが、顔の割合は小さく性能がいいとは言えません。
棒グラフにして見ても、割合を見た時にaltが優秀だったのが分かりやすいですね。「安安、お前スーパーマーケットですね(?)」
6.終わりに
今回、SKJvillageファンの方から収集したスクリーンショットから顔の認識を行い、認識した顔の切り抜きを行いました。切り抜いた顔は、SKJメンバーの顔に関する機会学習のために扱いたいと思います!
ご覧いただきありがとうございました!
参考