オンライン会議の相手をアバターにする

リアルタイム顔認識による全員アバター化計画の始まり

# 何を作ったか
画面上に表示されるウィンドウをキャプチャし、その顔を検出して画像ファイルに置き換えるPythonのプログラム「Avatarian（アバタリアン）」です。

![IMG_1989.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/ed481dc1-5b3e-e8c4-6b42-5fdc5653abe4.jpeg)

# この記事の内容
コードはface_recognitionのライブラリの使い方がわかればシンプルなので、使う人が苦労しそうな環境構築を中心に記載しています。
多くの人にこの Avatarian(アバタリアン)を使ってもらいたいです。

# Avatarianを作った動機

世の中オンライン会議が当たり前になったが、コロナ前のように顔を合わせたコミュニケーションが大事という雰囲気が高まりつつあり、オンライン会議で顔を出すことが求められるようになって来ているのではないだろうか。
Teams、Zoomなどのオンライン会議のアプリは、相手のカメラを個別にOFFにする機能がない。
このように、顔を見せたい側の都合が優先されるのは、コミュニケーションの前提として表情を重視する傾向があるためかもしれない
しかし、オンラインでは、目が合う、目を逸らすといったことは起こらない。そういうので機微を知るためにはいくら顔が並んでも意味がないのではないか。

また、そうであっても自分の顔を見は必要などあるだろうか？
私は未だかつて鏡を目の前に置いて自分の顔を見ながら会議をしているのを見たことがない。
オンラインだからといって自分の顔を画面に映す必要などあるはずがない。
自分の顔を見ながら会議をすることがストレスになるというスタンフォード大学ジェレミー・ベイレンソン教授の研究もある。
https://www.itmedia.co.jp/news/spv/2103/01/news066.html?ssp=1&darkschemeovr=1&setlang=ja&cc=JP&safesearch=moderate

そういうわけで、どうしてもカメラONを求められる会議でも、ストレスなく仕事の成果を上げるために、Avatarianは利用できると信じている。

というのは建前で、本当はメタバースの世界にZoomミーティングしている画面を表示する際に、リアルの人の顔が見えるとメタバース的に雰囲気を壊してアウトな気がしたので強制的にアバター化したかったので作ったものです。

# 要件
MoSCoW分析の手法でまとめてみる。

### Must(対応必須)
* 画面に表示されている顔をアバターに置き換える


### Should(対応すべき)
* 人よって別々の画像が選ばれるようにする
* ユーザが任意のアバター画像を利用できる
* 画像は顔のサイズに合うようにリサイズする
* 画面入力以外の入力として、カメラ入力に対応する
* ストリーム出力に対応する

### Could(できれば対応)
* 2Dアバターに対応する
* 人の表情をアバターの表情に反映する
* 人に応じたアバター画像を指定できる
* 人の顔に応じてアバターを生成することができる
* 3Dアバターに対応し、顔の向きや身振り手振りを反映する

### Won't(対応しない)
* 実行ファイル化
* Windows以外のOS対応(スマホ含む)

今回はShouldまで対応しています。

# 仕様検討
今回、Could要件にある表情の判定をしないため、顔認識出来ること(Must)と、顔の特徴量の抽出(Should)が重要になります。
また、AIモデルを使う上でリファレンスとなる記事の多いPythonで開発することにしました。

### 顔認識のライブラリ
顔認識のライブラリの選択については下記の記事を参考にしました。
顔を検出するだけでなく特徴に応じて画像を使い分けるためには、顔の特徴量抽出が必要になる。顔のランドマークと呼ぶようで、いくつかのライブラリがあります。それらのうち、face_recognitionとmediapipeを比較した結果として、face_recognitionの方が大人数の顔をリアルタイムで処理できた(GPUなし)ので、今回はface_recognitionで実装しています。

https://qiita.com/keisuke-okb/items/de7845dc695e3b432243


実装については下記の記事を参考にしました。

https://techpr.info/ml/opencv-dlib-face-recognition-part1/

https://qiita.com/mimitaro/items/bbc58051104eafc1eb38

https://ichi.pro/python-o-shiyoshita-kao-no-rando-ma-ku-kenshutsu-27920699623479

https://qiita.com/nonbiri15/items/f95b5fb01ae38980c9ce


# 環境
* OS:Windows10/11
* Python 3.12
* Microsoft C++ Build Tool 2022
face_recognitionで使っているdlibのインストールのために、利用

### 環境構築手順

face_recognitionはdlibという顔検出のモデルを使っているけれど、このライブラリは結構古くて、Windowsに標準では対応しておらず、face_recognitionのインストールのためには準備が必要です。

公式には下記の記載がありますが、個の記載も古いです（6年前！）
https://github.com/ageitgey/face_recognition#installing-on-windows

最新の環境で試した結果、企業でも(会社の会議でも)ライセンス的に問題なく利用可能な構成として、
Microsoft C++ Build Tool 2022
を使うことで、問題なく構築できました。

#### Microsoft C++ Build Toolのインストール

1. 下記からMicrosoft C++ Build Tool 2022をダウンロード
https://visualstudio.microsoft.com/ja/visual-cpp-build-tools/

2. インストールする
「C++によるデスクトップ開発」　にチェックを入れてインストール
![IMG_2041.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/d1082ac6-d2e5-a61b-54bb-f658ff9d7a9c.jpeg)


#### Pythonのインストール

1. 下記のサイトからPython 3.12.1をダウンロードする
https://www.python.org/downloads/release/python-3121/
Windows10/11の64bit版はこちら
https://www.python.org/ftp/python/3.12.1/python-3.12.1-amd64.exe

2. インストールする
「Add python.exe to PATH」 にチェックを入れてインストール
![IMG_2040.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/80a6db0f-fa7c-e244-c11a-3f3650cd5fbc.jpeg)


#### パスの確認

1. Developer Command Prompt for VS 2022を開く
![IMG_2045.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/d17ee960-4803-a3ab-955c-f90d20595d65.jpeg)

2. コマンドプロンプトで cmake と入力してエラーが出ないことを確認
3. コマンドプロンプトで Python -V と入力してエラーが出ないことを確認
![IMG_2046.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/317d7415-e73e-fece-c331-134e5cd23594.jpeg)

もしエラーが出た場合は、Pythonのインストール時にPATHの追加をするチェックを忘れているものと思うので、やり直してください。

#### Pythonライブラリのインストール
上記のDeveloper Command Prompt for VS 2022を開いた状態で

```
pip install opencv-python face_recognition pyautogui pygetwindow setuptools
```

とします。
しばらく待つと必要なライブラリがインストールされるはずです。
Python 3.10ではsetuptools不要でインストールできたのですが、Python 3.12.1ではこれがないとインストールに失敗しました。

# コードの全体構成と簡単な解説
最新のコードは
https://github.com/mtsnrtkhr/Avatarian
にあります。
ここでは、画面入力をもととしてアバター化するプログラムの解説をします。
「画面入力以外の入力として、カメラ入力に対応する」部分は、映像配信アプリのOBSとの兼ね合いも含めたいので、別途記事にしたいと思います。


1. ウィンドウ名を指定して位置を取得
2. スクリーンショットを取得
3. スクリーンショットの顔を検出
4. 顔の特徴を検出
5. 検出した順に、顔の特徴に応じてあらかじめ保存しているアバターの画像を割り当て
6. アバターの画像を顔部分に上書き
7. 描画

この構成が決まれば後は作るだけになりますが、Should要件「人よって別々の画像が選ばれるようにする」の部分である4，5がポイントとなるので少し解説します。


### 顔の特徴を検出
```python
# 画像から顔の位置を検出する
face_locations = face_recognition.face_locations(frame)

# 画像から顔の特徴を検出する
face_encodings = face_recognition.face_encodings(frame, face_locations)
```
frameは指定したアプリのスクリーンショット。これだけです。face_recognitionのライブラリの使い勝手の良さがよくわかります。

### 検出した順に、顔の特徴に応じてあらかじめ保存しているアバターの画像を割り当て
```python
# 画像を格納するためのリストを初期化
avatars = []

# アバター画像を読み込み、リストに追加
avatars = [cv2.imread(image_path) for image_path in avatar_images_paths]

# 顔の特徴に対応するアバター画像を保持する配列と辞書
known_face_encodings = []
face_to_avatar_index = {} 

# 検出した顔について繰り返し実行する
for index, face_encoding in enumerate(face_encodings):
    # 検出した顔が、以前のスクリーンショット中ですでに検出済みかどうかを判定
    matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance=0.6)

    if any(matches):
        # 検出済みの場合、画像との対応を保存した配列からアバター画像を特定
        matched_index = matches.index(True)
        avatar_index = face_to_avatar_index[matched_index]
        avatar_image = avatars[avatar_index]
    else:
        if len(avatars) > len(known_face_encodings):
            # アバター画像が残っている場合、新しい顔にアバター画像を割り当てる
            known_face_encodings.append(face_encoding)
            face_to_avatar_index[len(known_face_encodings) - 1] = len(known_face_encodings) - 1
            avatar_image = avatars[len(known_face_encodings) - 1]
        else:
            # アバター画像を使いきった場合、最後の画像を割り当てる
            avatar_image = avatars[-1]
```
特に何ということのない（Qiitaに書いてよいか迷う）シンプルなプログラムになっています。
ポイントは、
```
matches = face_recognition.compare_faces(known_face_encodings, face_encoding, tolerance=0.6)
```
の```tolerance=0.6```の部分で、これが高すぎると顔の区別ができなくなり、低すぎると同一人物でも少し角度が変わっただけで別人扱いになります。実験的には0.6が良い感じでした。


# 使い方
https://github.com/mtsnrtkhr/Avatarian
からファイルをZip形式でダウンロードして、展開後そのフォルダでコマンドプロンプトを起動して、
Windowsのタスクバーに表示されているウィンドウの名前を使って下記のコマンドを入力してください。
```
python main.py "ウィンドウの名前"
```

しばらくすると指定したウィンドウと同じサイズのアプリが起動します。

![IMG_1989.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/254192/ed481dc1-5b3e-e8c4-6b42-5fdc5653abe4.jpeg)

対象のアプリがメインディスプレイにないと使えないという制約があるので、OBSからのストリームにすると改善できるのですが、それは今後アップデートしたいと思います。

これでストレスフルな会議から解放されるはず。
皆さん、良いアバタリアンライフを！