1.はじめに
8/28付けMIT Technology Reviewによれば、日本のビデオゲームの挿入歌「ばかみたい」をネタにしたディープフェイク動画が最近ネット界隈で流行っていて、その背景には作成ツールが誰でも簡単に使えるようになった事があるとのことです。
その作成ツールに使われている技術は、2019年に発表された First Order Motion Model で、百聞は一見にしかず、こんなことが簡単に出来ます。
左が有村架純さんの静止画 、中央がトランプ大統領の動画、そして右が生成した有村架純さんがトランプ大統領のように動く生成動画です。
2.First Oder Motion Model とは?
これは、モデルの概要図で、大きく分けて Motion Module と Generation Module の2つで構成されています。入力は、静止画 source と、動画 Driving Frame です。
Motion Module は、静止画と動画の各フレームそれぞれに対して、キーポイント位置(顔の場合なら目、鼻、口、顔の輪郭など)、キーポイントにおける勾配を、Refarence画像(図ではRと表示)を基準として計算します。
そして、そのキーポイント計算とヤコビアンJk(静止画と動画フレームの大きさの比率)から、動画の各フレームの画素を静止画のどの画素から作れば良いかを示すマッピング関数を計算します。
この様に静止画と動画の各フレームの差を計算するのではなく、Refarence画像という概念的な基準画像との差を計算することで静止画と動画の各フレームが独立に計算出来、両者の画像の差が大きくても上手く変換ができます。
また、静止画を動かすとそもそも静止画に含まれていない部分が発生しますが、それを上手く処理するオクルージョン(Oで表示)という処理を行っています。
Generation Module は、それらの結果と静止画から、画像を出力します。損失関数は、静止画と出力画像の誤差+正則化項(マッピング関数が単純な変換に近くなるような拘束条件)です。
学習時は、静止画と動画は同一の動画から任意のフレームを選択して行います。推論時は、学習時と同じカテゴリーであれば静止画と動画とも任意のもので行えます(これがこのモデルの素晴らしいところです)。
3.学習済みモデルを使ってみる
今回のコードはGoogle Colabで動かす形にしてGithubに上げてありますので、それに沿って説明して行きます。自分で動かしてみたい方は、この「リンク」をクリックし表示されたノートブックの先頭にある「Colab on Web」ボタンをクリックすると動かせます。
学習済みモデルが提供されていますので、それを使って試してみましょう。まず、コードとサンプルデータをダウンロードします。詳細はGoogle Colabを参照下さい。
最初に、静止画、動画、生成動画をまとめて表示するための関数display()を定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import imageio import numpy as np import matplotlib.pyplot as plt import matplotlib.animation as animation from skimage.transform import resize from IPython.display import HTML import warnings warnings.filterwarnings("ignore") def display(source, driving, generated=None): fig = plt.figure(figsize=(8 + 4 * (generated is not None), 6)) ims = [] for i in range(len(driving)): cols = [source] cols.append(driving[i]) if generated is not None: cols.append(generated[i]) im = plt.imshow(np.concatenate(cols, axis=1), animated=True) plt.axis('off') ims.append([im]) ani = animation.ArtistAnimation(fig, ims, interval=33, repeat_delay=1000) plt.close() return ani |
この関数は、静止画、動画フレーム、生成動画フレームをconcatenate し、matplotlib の animation でアニメーション形式にしたものを返します。
4.モナリザをトランプ大統領のように動かす
静止画と動画は、顔のキーポイント位置(目、鼻、口、顔の輪郭など)が検出できれば絵でも人形でもアニメでも構わないので、ここでは静止画はモナリザ、動画はトランプ大統領を使ってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from demo import load_checkpoints from demo import make_animation from skimage import img_as_ubyte source_image = imageio.imread('./sample/05.png') driving_video = imageio.mimread('./sample/04.mp4') #Resize image and video to 256x256 source_image = resize(source_image, (256, 256))[..., :3] driving_video = [resize(frame, (256, 256))[..., :3] for frame in driving_video] generator, kp_detector = load_checkpoints(config_path='config/vox-256.yaml', checkpoint_path='./sample/vox-cpk.pth.tar') predictions = make_animation(source_image, driving_video, generator, kp_detector, relative=True) HTML(display(source_image, driving_video, predictions).to_html5_video()) |
この様に、モナリザをトランプ大統領の様に動かすことが簡単に出来ます。
5.オリジナル動画を使う
今度は、事前に用意した動画ではなく、オリジナルの動画を作成してやってみましょう。
サンプルにあるABCニュースの動画からキャスターの顔部分だけを切り出して、動画として使うことにしましょう。下記のコードを実行します。
1 |
!ffmpeg -i ./sample/news.mp4 -ss 00:00:00.00 -t 00:00:10 -filter:v "crop=200:200:550:80" -async 1 result.mp4 |
news.mp4から顔部分だけを切り出した200×200の動画をresult.mp4に保存します(動画は256×256以下にする必要があります)。result.mp4はこんなイメージです。
ffmpeg を使えば、1行でこんな顔を切り出す動画(クロップ動画)が簡単に作れます。引数を簡単に説明すると、
-i: 動画パス
-ss: 動画のクロップスタート時刻(時間:分:秒:1/100秒)
-t: クロップ動画の長さ(時間:分:秒)
-filter:v “crop=XX:XX:XX:XX”:(クロップ画像の横サイズ:クロップ画像の縦サイズ:クロップ画像の左上角の横位置:クロップ画像の左上角の縦位置)
*クロップ画像の左上角の横位置・縦位置は、動画の左上角を(0,0)として計算した位置です。
-async 1:クロップ画像保存パス
それでは、result.mp4 を動画にして5枚の静止画から、生成動画のフレームを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from demo import load_checkpoints from demo import make_animation from skimage import img_as_ubyte s0 = resize(imageio.imread('./sample/pic0.png'), (256, 256))[..., :3] s1 = resize(imageio.imread('./sample/pic1.png'), (256, 256))[..., :3] s2 = resize(imageio.imread('./sample/pic2.png'), (256, 256))[..., :3] s3 = resize(imageio.imread('./sample/pic3.png'), (256, 256))[..., :3] s4 = resize(imageio.imread('./sample/pic4.png'), (256, 256))[..., :3] source_image = [s0, s1, s2, s3, s4] driving_video = imageio.mimread('./result.mp4') driving_video = [resize(frame, (256, 256))[..., :3] for frame in driving_video] generator, kp_detector = load_checkpoints(config_path='config/vox-256.yaml', checkpoint_path='./sample/vox-cpk.pth.tar') result = [] for i in range(5): predictions = make_animation(source_image[i], driving_video, generator, kp_detector, relative=True) result.append(predictions) |
これで、5枚の静止画の生成動画フレームが作成できました。後は、これらと動画を連結します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
from tqdm import trange fig = plt.figure(figsize=(12, 8)) ims =[] for i in trange(len(result[0])): x = np.concatenate([result[0][i], result[1][i], result[2][i]],1) y = np.concatenate([result[3][i], driving_video[i], result[4][i]],1) z = np.concatenate([x, y]) im = plt.imshow(z, animated=True) plt.axis('off') ims.append([im]) print('making animeation...') ani = animation.ArtistAnimation(fig, ims, interval=33, repeat_delay=1000) plt.close() HTML(ani.to_html5_video()) |
これで作成完了です。
但し、作成した動画には元あった音声が消えていますので、QuickTimeなどでresult.mp4の音声を取り出し、作成した動画に追加してやると(これは手動で簡単にできます)。
こんな風に、ABCニュースのキャスターに合わせて、他の5名も喋り出します。
この様に、動画はWebからダウンロードした動画から顔部分を切り取ったものを使ってもOKですし、自分の顔動画をスマホで撮影してもOKです。
また、題材はスピーチでも、ドラマでも、歌でも何でもOKです。コードは省略しますが、歌を題材に作ったものも参考に添付しておきます。
では、また。
使いやすい改良版デモリンクの追加(2021.6.9): https://colab.research.google.com/github/AliaksandrSiarohin/first-order-model/blob/master/demo.ipynb
(Twiterへの投稿)
コメントを残す