SKJvillageのスクリーンショット内の顔認識

# １．あらすじ
　今回は導入として、pythonとOpenCVを用いて、ゲーム実況系Youtubeグループ[SKJvillage](https://www.youtube.com/@SKJVillage)のスクリーンショットからメンバー3人(その他の人物も含む)の顔認識を試みました！

# ２．顔認識とは
　顔認識とは、データの中から顔を検出することです。方式は人によって様々ですが、今回は顔認識を行うと顔の部分に赤枠が生成されます。

# ３．実験方法
　今回は、OpenCVに存在する分類器を用いてpythonで実行し、スクリーンショットに対する顔認識を行います。実行にはGoogle Colaboratoryを用います。プログラムを以下に示し、プログラムの後に詳細を説明します。
※Googleアカウントを持っていれば誰でも実行できます！

```python:SKJ_face_recognition.ipynb
!git clone https://github.com/opencv/opencv.git

import matplotlib.pyplot as pyplt
import cv2 as cv
import os

#カスケード分類器の読み込み
#HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_alt.xml"
HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_alt2.xml"
#HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_alt_tree.xml"
#HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_default.xml"

#カスケード分類器の特徴量を取得
cascade = cv.CascadeClassifier(HAAR_FILE)

#画像の読み込み
img_path = "ここに画像のパスを"
img=cv.imread(os.path.join(img_path),cv.IMREAD_UNCHANGED)
pyplt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))

# ダウンロードした画像の表示
pyplt.show()

#出力する画像の名前
output_img = "a.jpg"

#顔の認識
face_list = cascade.detectMultiScale(img, minSize=(1,1))

if len(face_list) == 0:
    # 顔が見つからなかったら
    print("失敗！")
else:
    # 顔が見つかったら、顔を赤く囲む
    img_mark = img.copy()
    for (x,y,w,h) in face_list:
        print("x座標,y座標,幅,高さ=", x, y, w, h)
        red = (0, 0, 255)
        cv.rectangle(img_mark, (x, y), (x+w, y+h), red, thickness=10)
    # 修正した画像を表示する
    cv.imwrite(output_img, img_mark)
    pyplt.imshow(cv.cvtColor(img_mark, cv.COLOR_BGR2RGB))
    pyplt.axis("off")
    pyplt.show()
```

## OpenCVのダウンロード
　OpenCVには、顔認識を行う分類器が数々設定されています。最初に下の1文を実行することで、github上の顔認識に関する分類器の特徴量を扱えます。
```python
!git clone https://github.com/opencv/opencv.git
```
## 分類器の取得
　分類器の選択を行い、読み込みと取得をすることで既存の分類器を扱うことができる。今回は、正面を向いている顔を検出できる、以下の4つの分類器で実験を行います。
・haarcascade_frontalface_alt.xml
・haarcascade_frontalface_alt2.xml
・haarcascade_frontalface_alt_tree.xml
・haarcascade_frontalface_default.xml
```python
#カスケード分類器の読み込み
#HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_alt.xml"
HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_alt2.xml"
#HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_alt_tree.xml"
#HAAR_FILE = "/content/opencv/data/haarcascades/haarcascade_frontalface_default.xml"

#カスケード分類器の特徴量を取得
cascade = cv.CascadeClassifier(HAAR_FILE)
```

## 画像を入力・顔を認識
　最後に実際に画像を入力し、メンバーの顔の認識を行います。`img_path`に実際に認識したい画像があるパスを記載する必要があります。今回はcolabを用いるため、Googleドライブに画像を配置しプログラム実行時にGoogleドライブにマウントさせ、Googleドライブ上の画像を参照するようにしました。
```python
#画像の読み込み
img_path = "ここに画像のパスを"
img=cv.imread(os.path.join(img_path),cv.IMREAD_UNCHANGED)
pyplt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))

# ダウンロードした画像の表示
pyplt.show()

#出力する画像の名前
output_img = "a.jpg"

#顔の認識
face_list = cascade.detectMultiScale(img, minSize=(1,1))

if len(face_list) == 0:
    # 顔が見つからなかったら
    print("失敗！")
else:
    # 顔が見つかったら、顔を赤く囲む
    img_mark = img.copy()
    for (x,y,w,h) in face_list:
        print("x座標,y座標,幅,高さ=", x, y, w, h)
        red = (0, 0, 255)
        cv.rectangle(img_mark, (x, y), (x+w, y+h), red, thickness=10)
    # 修正した画像を表示する
    cv.imwrite(output_img, img_mark)
    pyplt.imshow(cv.cvtColor(img_mark, cv.COLOR_BGR2RGB))
    pyplt.axis("off")
    pyplt.show()
```

## 出力結果
　実際にプログラムを実行した際の出力結果は下の画像のように、
・入力画像
・検出した座標
・認識できた顔を赤枠で囲んだ後の画像
の3つが順に出力されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128356/180b2175-b2c9-e374-3442-d2d7f492dbd4.png)

# ４．実験結果
　いくつかのSKJvillageのスクリーンショットに対して、4つそれぞれの分類器を用いて顔認識を行いました。全ての結果を載せてしまうと量が多くなってしまうため、代表的な出力結果を認識難易度の低い順に載せていきます。

### ①スマブラSPの背景で冒頭のあいさつ
haarcascade_frontalface_alt2.xml の分類器を使用
![arigatou-detect2 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128356/7dd6e4c5-b5a7-07d3-a4ea-4de90834e277.png)
　この画像は**メンバー3人は正面を向いているため、比較的簡単に顔を認識できました。** ちゃんとメンバー3人の顔に赤枠が生成されており、背景のスマブラSPの一部の人間キャラ(WiiFitトレーナー、リトルマック、カムイ)にも赤枠が生成されています。真っ白な顔のWiiFitトレーナーはなぜ？

### ②グレーに編集されたシーン
haarcascade_frontalface_alt.xml の分類器を使用
![owatta-detect.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128356/ae48ee82-f3c1-9f2a-4ad9-2640f40a7db5.jpeg)
　果たして彼らに何があったんでしょうか(笑)
　絶望を表すために字幕以外がグレーになっている画像です。**グレー画像になっていてもちゃんと3人の顔が認識できています。**

### ③増殖した斜めアングルのsa2
haarcascade_frontalface_default.xml の分類器を使用
![d-detectdef.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128356/42ea67d5-c34d-465a-419f-98ab0aac9f9e.png)
　メンバーカラー青のsa2は動画の中で増殖することが多いです。顔のアングルが正面ではなく斜めになっていますが、左2つの顔には赤枠が生成されています。しかし一番右には生成されていません。取りこぼしはありますが、**顔の全体像が映っていれば認識できることが分かります。**

### ④増殖した上を上を向いているsa2
haarcascade_frontalface_alt2.xml の分類器を使用
![abc-detect2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128356/965495ed-a145-d8c3-a05a-ba2641a69a5a.png)
　またかよって感じですよね(笑)さすがファンから妖怪って呼ばれてるだけあります(?)
　今度はsa2が上を向いていますが、**一部は顔の輪郭に沿うように赤枠を生成しています。しかし、多く赤枠は鼻の部分に生成されています。** どうやら鼻の穴を目と勘違いしており、誤認識しています。

#### ※SKJファンの皆さんへ
　SKJファン御用達の「タノシミサイカツ」「満月ヤバ男」「ガマシン」は正しく顔認識されませんでした。。。やっぱり人間じゃなくて妖怪だったのは本当のようだ。。。(時間があるときにまたトライします)

# ５．結果から。。。
　今回の実験で感じたことを4点挙げます。
## ①サングラスがあっても認識できる
　本来は目が隠れていない画像から分類器を作成していると考えれますが、**サングラスで目が隠れている場合でも正面を向いていれば認識可能なことが分かりました。**

## ②正面を向いていない顔は難しい
　動画のスクリーンショットであったことから、メンバーが正面を向いていない場合が多く存在します。**正面を向いていない場合でも、顔と分かれば認識できることもあればできない場合があるようです。**

## ③haarcascade_frontalface_alt2.xml の分類器が優秀
　今回は4つの分類器をそれぞれのデータに対して扱いましたが、今回検証したデータに対して**万能に顔を認識できていたのはhaarcascade_frontalface_alt2.xmlでした。** 今後はこれをベースにしようかなと考えています。

## ④文字や物体を顔と誤認識
　下の画像のように、**文字や物体を顔と誤認識している場合がありました。**「ら」「る」は丸みを帯びた文字(?)であることから顔と間違えたのかなと感じています。缶に関しては謎です。
![deisui-detectdef.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3128356/1124a57a-564a-4674-2906-1c620ea6a4e1.jpeg)

# ６．おわりに
　今回は簡易的ではありますが、[SKJvillage](https://www.youtube.com/@SKJVillage)のスクリーンショットを使ってサングラスをかけている人物を顔認識できるのかの検証を行いました。今後は「SKJvillageのメンバーの顔の学習をさせた後に、メンバーの顔のみを検出できるようにする」といったことを行いたいと考えています。

**最後までお付き合いいただきありがとうございました！**

※前回の記事も是非！！

https://qiita.com/nyar_ramen/items/74694fa1eb313e002450

# 参考
https://office54.net/python/opencv/image-face-authentication