【Face Swapping】推しとの思い出2022を"作る"

[MYJLab Advent Calendar 2022](https://qiita.com/advent-calendar/2022/myjlab)　1日目の記事です!
私は[MYJLab Advent Calendar 2019](https://qiita.com/advent-calendar/2019/myjlab)から毎年参加しており, 今年ついに初日を勝手に担当します🌟

## これはなに
突然ですが, みなさんに「推し」はいますか？私は2022年は推し活に勤しんだ1年でした！
ただ残念なことに, 私の推しはそこそこ有名で遠い存在です. なので, 2ショットが撮れたり, サインがもらえたりと, 推しとの思い出を形に残すことができる界隈が羨ましいです...
ということで, この記事では自分で推しとの思い出2022を作ってみたいと思います!!!(狂気)

## 方法
作るといっても推しに突撃しに行くわけにもいかないので, 「AI」の力を借りようと思います. 
方法は至って単純で, 推しと推し以外の人が写っている人の画像を用意し, 推しじゃない方の人の顔を自分の顔と差し替えてしまいます. 
顔画像の差し替え・交換はディープラーニングを用いて学習したモデルがいくつか公開されています. 
その中でも今回は[SberSwap](https://github.com/ai-forever/ghost)というモデルを用います. 
この手法を選んだ理由は以下の2点です.
- 1つ目は従来のモデルは個別に学習が必要で, 処理時間が長くなってしまう課題があったが, SberSwapは学習不要ですぐに使うことができ, 処理がとても早い
- [デモ](https://colab.research.google.com/github/cedro3/sber-swap/blob/main/FaceSwap.ipynb)がColabolatory上に公開されているので, すぐに動かすことができる

今回はデモのcolabを少し書き換えただけで, すぐに目的が達成できてしまいました!
以降で書き換えた部分と使い方について説明します🌟

### 変更した部分
デモは, 写真に人が1人しか写っていないことが前提で実装されていました. 
今回はターゲット画像に推しと推し以外の人物が写っていて, しかも推しじゃない方の人物と顔を交換する必要があります. そのため, まずはライブラリに用意されていた`crop_face`という関数を書き換えて, 複数人の顔を検知するようにしました. 

```python
def crop_face(image_full: np.ndarray, app, crop_size: int) -> np.ndarray:
    align_imgs = []
    kps = app.get(image_full, crop_size)
    for k in kps:
        M, _ = face_align.estimate_norm(k, crop_size, mode ='None') 
        align_img = cv2.warpAffine(image_full, M, (crop_size, crop_size), borderValue=0.0)
        align_imgs.append(align_img)        
    return align_imgs
```

`crop_face`は以下のように使われます. 

```python
# ターゲット画像のパスを指定
target_path = 'examples/images/target.png'
crop_size = 224
# 画像を読み込む
target_full = cv2.imread(target_path)
# 顔を検出する
targets = crop_face(target_full, app, crop_size)
print("推しが写っている画像から検出された顔\n")

show_images(
    targets,
    list(map(lambda x: x+1, range(len(targets)))),
    figsize=(20, 15)
)
```
例えば以下の画像を入力した場合, 

![oshi.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/4b6bc6c4-c8cb-4961-b870-ea2696c7a37c.jpeg)

以下のように複数の顔が検出されます. 
![faces.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/71721983-6d14-c5fb-d289-3651ad64abd4.png)

次に, 検出された複数の顔のうち, どの顔と自分の顔を交換するかを選択します. 
このデモはColabの[Forms](https://colab.research.google.com/notebooks/forms.ipynb)という機能を使って入力を受け取り, リアルタイムにコードに反映させることで, Webアプリのようにデモを体験できるようになっています. 
顔を選択する際にも, この機能を用いて, 何番の画像と交換するかを入力してもらうようにしました.

具体的には, 以下のようなコードを書くと, 

```python
#@markdown target_idに入れ替えたい画像のタイトル（番号）を入力してください
#@markdown 入力ができたら実行ボタンを押してください

target_id = 1 #@param {type:"integer"}
target = [targets[target_id-1]]
```

以下のようなFormが同時に作成され, 入力が`target_id`という変数にリアルタイムに反映されます.
![form.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/bdf883fa-3dcd-397d-d461-b99986b778e4.png)

変更点はほぼこれだけです! あとはnotebookをぽちぽち進めるだけで推しとの思い出の写真を作り出すことができます.

## いざ, 思い出生成
自分の顔画像で生成した結果を載せたいところですが, 黒歴史になりそうなのでフリーの画像(sample.jpg)を使わせていただきます.
![sample.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/0b5ad0f0-d1de-2ad6-31cd-28cf4f643cc6.jpeg)

こちらの美しい女性のお顔を, 先ほど顔検出の例として使った画像(oshi.jpeg)の女性のお顔に合成します.
まず, `examples/images`に画像をアップロードします. ちなみに`examples/images`にはデモ用のサンプル画像があらかじめ用意されているので, そちらを使えば画像を用意することなく試すことができます.
![dir.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/89d5c2ab-2bcf-f83b-c4fa-020c1435da4f.png)

次に, アップロードした画像のパスをフォームに入力し, 実行します.
![form2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/c1711ff3-7c14-1a08-8ed5-49879d55a176.png)

実行すると検出した顔画像が表示されるので, 上記に書いた手順で, どの顔と自分の顔を交換するかを入力します.
あとはぽちぽち進めると結果が表示されます!
![result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/d03ba9ee-9ea3-09a5-8cf7-902a4be26463.png)

## まとめ
既存のコードを少し書き換えるだけで, 推しとの思い出を作ることができてしまいました. プログラミングを勉強していて本当によかったです😃
また, 今回ColabのFormsを初めて使ったのですが, リソースの問題でなかなかAPIやアプリとして公開するのが難しい機械学習モデルのデモを, 簡単かつGoogleさんのGPUを使ってWebアプリのように試せるのはかなり便利だなと思いました!
今回書き換えたcolabノートブックは[こちら](https://colab.research.google.com/drive/1w7SWDubmqg_kRsKQeH-l2laG5L13LsIJ?usp=share_link)に置いておきます🌟
## 参考・出典
- https://colab.research.google.com/github/cedro3/sber-swap/blob/main/FaceSwap.ipynb
- https://qiita.com/pokohide/items/43203f109fd95df9a7cc
- https://qiita.com/DeepRecommend/items/2663e9b73b4bd4f7037d
- https://www.photo-ac.com/main/detail/24275723
- https://github.com/ai-forever/ghost
- https://colab.research.google.com/notebooks/forms.ipynb
- http://cedro3.com/ai/sberswap/