私的AI研究会 > GAN-e4e
「StyleGAN」で画像編集する。
StyleGAN を使った画像編集では「潜在変数の推定」プロセスに時間が掛かる。
これをを高速に実行できる e4e(encoder4editing)という技術について、上記サイトに従い検証してみる。
e4e は StyleGAN の学習済みモデルを使用して、画像を入力すると目的の潜在変数を直接出力する専用エンコーダを作る手法。Encoderに画像を入力すると1つの潜在変数 w とN個のオフセット△が出力され、これらを合成し N個の潜在変数とし Pretrained StyleGAN に入力する。このとき、元画像と出力画像の誤差を表すロスに加えて、オフセットの分散を表すロスを設定し、これら2つのロスの合計を最小化するように、Encoder のパラメータを学習する。
# --- セットアップ ---
import os
os.chdir('/content')
CODE_DIR = 'encoder4editing'
!git clone https://github.com/cedro3/encoder4editing.git $CODE_DIR
!wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip
!sudo unzip ninja-linux.zip -d /usr/local/bin/
!sudo update-alternatives --install /usr/bin/ninja ninja /usr/local/bin/ninja 1 --force
os.chdir(f'./{CODE_DIR}')
from argparse import Namespace
import time
import os
import sys
import numpy as np
from PIL import Image
import torch
import torchvision.transforms as transforms
sys.path.append(".")
sys.path.append("..")
from utils.common import tensor2im
from models.psp import pSp # we use the pSp framework to load the e4e encoder.
%load_ext autoreload
%autoreload 2
# 学習済みパラメータのダウンロード
! pip install --upgrade gdown
import os
import gdown
os.makedirs('pretrained_models', exist_ok=True)
gdown.download('https://drive.google.com/u/1/uc?id=1Du_8FzOPKJhk6aJmiOBhAWVe3_6vAyET', 'pretrained_models/e4e_ffhq_encode.pt', quiet=False)
# ランドマークデータのダウンロード
! wget http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
! bzip2 -dk shape_predictor_68_face_landmarks.dat.bz2
# モデルに学習済みパラメータをロード
model_path = 'pretrained_models/e4e_ffhq_encode.pt' ####
ckpt = torch.load(model_path, map_location='cpu')
opts = ckpt['opts']
opts['checkpoint_path'] = model_path
opts= Namespace(**opts)
net = pSp(opts)
net.eval()
net.cuda()
print('Model successfully loaded!')
# --- 顔画像の切り出し ---
import os
import shutil
from tqdm import tqdm
if os.path.isdir('align'):
shutil.rmtree('align')
os.makedirs('align', exist_ok=True)
def run_alignment(image_path):
import dlib
from utils.alignment import align_face
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
aligned_image = align_face(filepath=image_path, predictor=predictor)
return aligned_image
path = './images'
files = sorted(os.listdir(path))
for i, file in enumerate(tqdm(files)):
if file=='.ipynb_checkpoints':
continue
input_image = run_alignment(path+'/'+file)
input_image.resize((256,256))
input_image.save('./align/'+file)
# --- 潜在変数の推定 ---
if os.path.isdir('vec_pic'):
shutil.rmtree('vec_pic')
os.makedirs('vec_pic', exist_ok=True)
if os.path.isdir('vec'):
shutil.rmtree('vec')
os.makedirs('vec', exist_ok=True)
img_transforms = transforms.Compose([
transforms.Resize((256, 256)),
transforms.ToTensor(),
transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])
path = './align'
files = sorted(os.listdir(path))
for i, file in enumerate(tqdm(files)):
if file=='.ipynb_checkpoints':
continue
input_image = Image.open(path+'/'+file)
transformed_image = img_transforms(input_image)
with torch.no_grad():
images, latents = net(transformed_image.unsqueeze(0).to('cuda').float(), randomize_noise=False, return_latents=True)
result_image, latent = images[0], latents[0]
tensor2im(result_image).save('./vec_pic/'+file) # vec_pic 保存
torch.save(latents, './vec/'+file[:-4]+'.pt') # vec 保存
%matplotlib inline・変更した以下のセルを実行する
# --- 元画像と生成画像の表示 ---
%matplotlib inline
import matplotlib.pyplot as plt
from PIL import Image
import os
def display_pic(folder):
fig = plt.figure(figsize=(30, 40))
files = os.listdir(folder)
files.sort()
for i, file in enumerate(files):
img = Image.open(folder+'/'+file)
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(folder+'/'+file, fontsize=15)
plt.show()
plt.close()
display_pic('align')
display_pic('vec_pic')
・上段が切り出した顔画像、下段が潜在変数から生成した画像~#@title 設定
latent = "03.pt"#@param {type:"string"}
direction = "pose" #@param ["age", "pose", "smile", "age+pose"] {allow-input: true}
min = -50 #@param {type:"slider", min:-50, max:0, step:10}
max = 50 #@param {type:"slider", min:0, max:50, step:10}
# --- 静止画の生成 ---
import os
import shutil
if os.path.isdir('pic'):
shutil.rmtree('pic')
os.makedirs('pic', exist_ok=True)
from editings import latent_editor
from tqdm import trange
folder = 'vec'
latents = torch.load(folder+'/'+latent)
editor = latent_editor.LatentEditor(net.decoder, False)
interfacegan_directions = {
'age': 'editings/interfacegan_directions/age.pt',
'smile': 'editings/interfacegan_directions/smile.pt',
'pose': 'editings/interfacegan_directions/pose.pt',
'age+pose': 'editings/interfacegan_directions/age+pose.pt'
}
interfacegan_direction = torch.load(interfacegan_directions[direction]).cuda()
cnt = 0
for i in trange(0, min, -1, desc='0 -> min'):
result = editor.apply_interfacegan(latents, interfacegan_direction, factor=i).resize((512,512))
result.save('./pic/'+str(cnt).zfill(6)+'.jpg')
cnt +=1
for i in trange(min, max, desc='min -> max'):
result = editor.apply_interfacegan(latents, interfacegan_direction, factor=i).resize((512,512))
result.save('./pic/'+str(cnt).zfill(6)+'.jpg')
cnt +=1
for i in trange(max, 0, -1, desc='max -> 0'):
result = editor.apply_interfacegan(latents, interfacegan_direction, factor=i).resize((512,512))
result.save('./pic/'+str(cnt).zfill(6)+'.jpg')
cnt +=1
# --- mp4動画の作成 ---
# 既に output.mp4 があれば削除する
import os
if os.path.exists('./output.mp4'):
os.remove('./output.mp4')
# pic フォルダーの静止画から動画を作成
! ffmpeg -r 30 -i pic/%6d.jpg\
-vcodec libx264 -pix_fmt yuv420p output.mp4
# movieフォルダへ名前を付けてコピー
import shutil
os.makedirs('movie', exist_ok=True)
shutil.copy('output.mp4', 'movie/'+direction+'_'+latent[:-3]+'.mp4')
# --- mp4動画の再生 ---
from IPython.display import HTML
from base64 import b64encode
mp4 = open('./output.mp4', 'rb').read()
data_url = 'data:video/mp4;base64,' + b64encode(mp4).decode()
HTML(f"""
<video width="50%" height="50%" controls>
<source src="{data_url}" type="video/mp4">
</video>""")