0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

SKJvillageのスクリーンショットで既存の顔のカスケード分類器を評価

Posted at

1.あらすじ

 今後の顔分類の実験の準備として、OpenCVのカスケード分類器を用いてゲーム実況系YoutubeグループSKJvillageのスクリーンショットから顔のみを切り抜く処理を行いました。複数の分類器で顔を切り抜きを行いましたが、本件のデータに対しては分類器ごとに向き不向きがあると感じたので、今回は実際に切り抜いた画像に注目し性能評価を行いました。

2.改めてSKJvillageとは

3人組のゲーム実況系YouTubeチャンネル。高校時代の親友で「早稲田・慶應・上智」大学にそれぞれ進学し、大手企業を退職後、2018年に3人でYoutube活動開始。
(https://www.youtube.com/@SKJVillage/about より参考)

3.顔を切り抜くためのプログラム

 今回は、Google Colaboratory上でpythonとOpenCVを用いて、SKJvillageのスクリーンショットから顔を探し、顔だけを切り抜いた画像を生成しました。

SKJ_FaceClipping.ipynb
!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の画像を切り抜くことができました。ちゃんとメンバーの顔を切り抜けている場合もあれば、謎の画像が生成されている場合もありますね。(リトルマックイケメンすぎ問題)
image.png
 出力は以下のように、作成したデータセットのカテゴリごとに出力されます。データセット、カテゴリについては、画像の下の記事から!
image.png

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は多くの画像の出力していますが、顔の割合は小さく性能がいいとは言えません。

image.png
 棒グラフにして見ても、割合を見た時にaltが優秀だったのが分かりやすいですね。「安安、お前スーパーマーケットですね(?)」

6.終わりに

 今回、SKJvillageファンの方から収集したスクリーンショットから顔の認識を行い、認識した顔の切り抜きを行いました。切り抜いた顔は、SKJメンバーの顔に関する機会学習のために扱いたいと思います!
 ご覧いただきありがとうございました!

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?