1.はじめに
「Toonify Yourself」という自分の顔をアニメ顔に変換してくれるWebサービスが大人気です。但し、あまりにもアクセスが集中したためサーバーコストがかさみ現在はサービスが中断されています。
自分の顔をアニメ顔に変換できるなんて面白いと思いませんか?やってみたいがサービス中止では諦めるしかないと思ったあなた、心配いりません。
コードは公開されていますので、今回は、Webサービスを使わずに自分のアニメ顔を作る方法をご紹介します。
2.Toonify Yourselfとは?
Toonify Yourself は、2つのStyleGAN2を組み合わせています。1つは、FFHQの顔画像70,000枚を学習したBaseモデル、もう1つはBaseモデルに300枚のアニメ顔をファインチューニングしたFine turningモデルです。
BaseモデルとFine turningモデルの両方に同じ潜在変数w(1,18,512)を入力すると、Fine turningモデルの出力にはBaseモデルの出力と相関が高いアニメ顔が現れます。しかし、学習するアニメデータはCGや手書きの混在しているためアニメ顔の画質は低いです。そこで、この2つのモデルを組み合わせます。
低解像度(顔の向きや形などに影響)にFine turningモデルを使い、高解像度(照明やテクスチャなどに影響)にBaseモデルを使うことで、リアルなアニメ顔の出力が得られます。これをBlendedモデルと呼びます。
StyleGAN2では、任意の顔の潜在変数wの探索が可能ですので、任意の顔のアニメ顔への変換が可能になるわけです。
3.コード
今回のコードもGoogle Colabで動かすような形にしてGithubに上げてありますので、それに沿って説明して行きます。自分で動かしてみたい方は、この「リンク」をクリックし表示されたノートブックの先頭にある「Colab on Web」ボタンをクリックすると動かせます。
最初に、セットアップを行います。
1 2 |
%tensorflow_version 1.x !git clone https://github.com/justinpinkney/stylegan2 |
1 2 3 4 5 6 |
%cd stylegan2 !nvcc test_nvcc.cu -o test_nvcc -run # フォルダーの作成 !mkdir aligned !mkdir generated |
ffhqモデル(Baseモデル)とblendedモデルをダウンロードします。
1 2 3 4 5 6 7 8 9 10 11 12 |
# Baseモデル(ffhqモデル)とblendedモデルのダウンロード import pretrained_networks import gdown blended_url = "https://drive.google.com/uc?id=1BRqqHWk_4BjNHLXTrpHkoxmZhGw3CU59" ffhq_url = "http://d36zk2xti64re0.cloudfront.net/stylegan2/networks/stylegan2-ffhq-config-f.pkl" gdown.download(blended_url, quiet=False) gdown.download(ffhq_url, quiet=False) _, _, Gs_blended = pretrained_networks.load_networks('/content/Toonify-Yourself/ffhq-cartoon-blended-64.pkl') _, _, Gs = pretrained_networks.load_networks('/content/Toonify-Yourself/stylegan2-ffhq-config-f.pkl') |
必要な関数を定義します。
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# 画像表示 import matplotlib.pyplot as plt from PIL import Image import glob import numpy as np def display_pic(folder): fig = plt.figure(figsize=(30, 40)) files = glob.glob(folder) files.sort() images=[] for i in range(len(files)): img = Image.open(files[i]) images = np.asarray(img) ax = fig.add_subplot(10, 10, i+1, xticks=[], yticks=[]) image_plt = np.array(images) ax.imshow(image_plt) ax.set_xlabel(str(i), fontsize=20) plt.show() plt.close() # 潜在変数(latents)から生成した画像を表示 import PIL.Image def display(latents): synthesis_kwargs = dict(output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=False), minibatch_size=8) fig = plt.figure(figsize=(30, 40)) for i in range(len(latents)): vec = latents[i].reshape(1,18,512) images = Gs_blended.components.synthesis.run(vec, randomize_noise=False, **synthesis_kwargs) images = images.transpose((0,2,3,1)) PIL.Image.fromarray(images[0], 'RGB') ax = fig.add_subplot(10, 10, i+1, xticks=[], yticks=[]) image_plt = np.array(images[0]) ax.imshow(image_plt) ax.set_xlabel(str(i), fontsize=20) plt.show() plt.close() # 潜在変数(latents)の順番を指定して、トランジションのGIFを作成する import os def generate_gif(latents, idx): synthesis_kwargs = dict(output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=False), minibatch_size=8) image_gif = [] os.makedirs('my/gif', exist_ok=True) for j in range(len(idx)-1): for i in range(20): latent = latents[idx[j]]+(latents[idx[j+1]]-latents[idx[j]])*i/19 latent = latent.reshape(1, 18, 512) images = Gs_blended.components.synthesis.run(latent, randomize_noise=False, **synthesis_kwargs) images = images.transpose((0,2,3,1)) image_one = PIL.Image.fromarray(images[0], 'RGB') image_gif.append(image_one.resize((256,256))) image_gif[0].save('./my/gif/anime.gif', save_all=True, append_images=image_gif[1:], duration=100, loop=0) |
まず、アニメ化したい顔が写っている画像(rawフォルダーにあるサンプル)から、顔部分を切り出します。
1 2 3 |
# 顔画像の切り取り !python align_images.py raw aligned display_pic('./aligned/*.png') |
rawフォルダーにある画像から、dlibを使いalign(顔が直立するように回転させる)し、設定範囲を切り取った画像(*.png)をalignedフォルダーに保存します。
※自分の顔の写った画像など自分のオリジナルの画像をアニメ顔に変換したい場合は、その画像をrawフォルダーに保存しておいて下さい。
次に、切り取った顔画像を生成する潜在変数wを見つけます。
1 2 3 |
# 顔画像の潜在変数を求める !python project_images.py --num-steps 500 aligned generated display_pic('./generated/*.png') |
alignedフォルダーの顔画像に出来るだけ似た顔画像を生成する潜在変数wを求め、潜在変数を generate/*.npy に保存し、潜在変数wから生成した画像を generate/*.png に保存します。表示されているのは、潜在変数wから生成した画像です。
そして、潜在変数wを元に、Blendedモデルでアニメ画像を生成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# 潜在変数wからアニメ画像を生成する import numpy as np from PIL import Image import dnnlib import dnnlib.tflib as tflib from pathlib import Path latent_dir = Path("generated") latents = sorted(latent_dir.glob("*.npy")) for i, latent_file in enumerate(latents): latent = np.load(latent_file) latent = np.expand_dims(latent,axis=0) synthesis_kwargs = dict(output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=False), minibatch_size=8) images = Gs_blended.components.synthesis.run(latent, randomize_noise=False, **synthesis_kwargs) Image.fromarray(images.transpose((0,2,3,1))[0], 'RGB').save(latent_file.parent / (f"{latent_file.stem}-toon.jpg")) if i == 0: latents = latent else: latents = np.concatenate([latents, latent]) # 潜在変数(latents)から生成した画像を表示する display(latents) |
潜在変数w(generated/*.npy)を読み込み、Blendedモデルでアニメ画像を生成しgenerated/*-toon.jpgで保存し、使用した潜在変数は全て変数latentsに保存します。
最後に、変数latentsからトランジションを作成します。
1 2 3 4 |
# 潜在変数(latents)の順番を指定して、トランジションGIFを作成する from IPython.display import Image generate_gif(latents,[0,1,2,0]) Image('./my/gif/anime.gif', format='png') |
潜在変数 latnts の0番目→1番目→2番目→0番目にトランジションするGIFです。my/gif/anime.gifに保存されます。
色々な顔画像で試してみて楽しんで下さい。では、また。
(Twitterへの投稿)
cedro様
お世話になります。
こちらのToonify Yourselfがすごいと思い、cedro3さんのコードをGoogle Colabで実行したのですが、
BaseモデルとBlendedモデルのダウンロードのところでエラーがでました。
_, _, Gs_blended = pretrained_networks.load_networks(blended_url)
—> 76 G, D, Gs = pickle.load(stream, encoding=’latin1′)
UnpicklingError: invalid load key, ‘<'.
と出てきまして、picklをloadできませんでした。
試しに https://www.justinpinkney.com/toonify-yourself/
からもコードを実行しましたが、同じエラーが出てきます。
このような場合はどこに問題があるかをお教えいただけませんでしょうか?
お忙しいところ恐縮ですが、よろしくお願いいたします。
cedro様
お世話になります。解決できました。picklをダウンロードし、その後、Toonify Yourselfの直下に置きました。そして10行目の_._.Gs_blended = pretrained_networks.load_networks(blended_url)の()内をpicklに置き換えたら、loadすることができました。
cedroさんのブログでは、興味深い内容のものばかりで、勉強させていただいております。今後のご活躍を期待しております。
ピーチママさんのコメント通りで動きました。
コード(Colab用)を載せておきますね。
# ffhqモデル(Baseモデル)とblendedモデルのダウンロード
import pretrained_networks
import gdown
# use my copy of the blended model to save Doron’s download bandwidth
blended_url = “https://drive.google.com/uc?id=1BRqqHWk_4BjNHLXTrpHkoxmZhGw3CU59”
ffhq_url = “http://d36zk2xti64re0.cloudfront.net/stylegan2/networks/stylegan2-ffhq-config-f.pkl”
#モデルのダウンロード ↓↓これを追加する
gdown.download(blended_url, quiet=False)
gdown.download(ffhq_url, quiet=False)
#Python2で作成されたpklをPython3で使えるように変換
_, _, Gs_blended = pretrained_networks.load_networks(“/content/stylegan2/ffhq-cartoon-blended-64.pkl”)
_, _, Gs = pretrained_networks.load_networks(“/content/stylegan2/stylegan2-ffhq-config-f.pkl”)
ピーチママさん、ラドリーさん
情報ありがとうございました。コードを修正しました ^^