私的AI研究会 > GAN-e4e

StyleGANを使った画像編集:StyleGAN e4e

 「StyleGAN」で画像編集する。

※ 最終更新:2023/12/17 

サイト『StyleGANを使った画像編集をe4eで高速化する』 の検証

概要

 StyleGAN を使った画像編集では「潜在変数の推定」プロセスに時間が掛かる。
これをを高速に実行できる e4e(encoder4editing)という技術について、上記サイトに従い検証してみる。
e4e は StyleGAN の学習済みモデルを使用して、画像を入力すると目的の潜在変数を直接出力する専用エンコーダを作る手法。

 Encoderに画像を入力すると1つの潜在変数 w とN個のオフセット△が出力され、これらを合成し N個の潜在変数とし Pretrained StyleGAN に入力する。このとき、元画像と出力画像の誤差を表すロスに加えて、オフセットの分散を表すロスを設定し、これら2つのロスの合計を最小化するように、Encoder のパラメータを学習する。

Google Colaboratory に実行環境を作成

  1. 上記サイト作者の デモサイト を開き「Open in Colab」① ボタンを押す

  2. 『e4e_demo』の Google Colab が開くので「ファイル」メニューから「ドライブにコピーを保存」を選択

  3. 『e4e_demo のコピー』のタイトルで開いた Google Colab のページで以降の操作を行う

  4. データファイルをダウンロードして解凍する(解凍した「update/work/gan_e4e/」を使用する
     update_20231117.zip (18.3MB) <アップデート・データ>

環境設定

  1. 以下のセルを実行する ①
    # --- セットアップ ---
    
    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!')
    ▼ - log - GoogleColab Tesla T4

  2. セルの実行終了② 後、左サイドバーの「ファイルボタン」を押す
    「encoder4editing」③ の下の「images」」④ フォルダがあることを確認する
  1. 「images」フォルダに用意した顔画像を追加(ローカルマシンの画面からドラッグ&ドロップで OK)

事前準備

  1. 顔画像の切り出し
    ・StyleGAN が学習した顔画像に合わせてサンプル画像から顔を切り出し「./align」フォルダに同じファイル名で保存(顔のランドマーク(目・鼻・口など)が所定の位置にある)
    ・ 以下のセルを実行する
    # --- 顔画像の切り出し ---
    
    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)
    ▼ - log - GoogleColab Tesla T4

  2. 潜在変数の推定 ・切り出した顔画像から潜在変数を推定しこれから生成した画像は同じファイル名で「./vec_pic」に保存する
    ・以下のセルを実行する
    # --- 潜在変数の推定 ---
    
    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  保存
    ▼ - log - GoogleColab Tesla T4

  3. 元画像と生成画像の表示
    ・推定した潜在変数は同じファイル名(拡張子はjpg → pt)で 「./vec」に保存さる
    下記の 1行をセルのコード最初に追加する
    %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')
    ・上段が切り出した顔画像、下段が潜在変数から生成した画像~
    ・切り出した顔画像とほぼ同じ画像を生成できる潜在変数が素早く推定できたことが分かる
    ・これで、編集できる潜在変数を得ることができたことになる

画像編集

  1. 設定
    ・以下のセルを実行する
    #@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}

  2. 静止画の生成
    ・以下のセルを実行する
    # --- 静止画の生成 ---
    
    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
    ▼ - log - GoogleColab Tesla T4

    ・潜在変数 age に掛ける数字を少しづつ変化させながら、StyleGAN が出力する静止画を保存する。
    ・0 から min まで変化させ、次に min から max まで変化させ、最後に max から 0 まで変化させる。
    ・静止画は「000001.jpg」という形の6桁の連番名で「./pic」に保存する

  3. 動画の生成
    ・「./pic」に保存された静止画から mp4 動画を作成する
    ・以下のセルを実行する
    # --- 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')
    ▼ - log - GoogleColab Tesla T4

    ・作成された動画は「output.mp4」で保存すると共に「./movie」に「<画像ファイル名>.mp4」という形で保存する

  4. 動画の再生
    ・作成した「output.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>""")

    ・作成画像を取得する場合は「./movie/<画像ファイル名>.mp4」をダウンロードする

別の画像を編集

  1. latent に編集する画像の潜在変数「<画像名>.pt」入力
  2. 編集する潜在変数 「age / pose / smile/ age+pose」を選択する
  3. min / max を設定(smile の場合は、少し弱目にで min 0 max 30 位で)
  4. Colab「ランタイム」メニューから「以降のセルを実行」を選んで実行する

編集の終了・再接続後の実行

  1. 編集を終えるときは Colab「ランタイム」→「ランタイムを接続解除して削除」を選択する
    ・GPU 占有時間を少なくするためすべての実行作業が終了した場合は接続解除しておくことが望ましい
    ・接続解除して削除を実行しても、ノートブック上の実行結果はそのまま残る

  2. 再接続の場合は上記の 環境設定 からもう一度実行同じ手順をする

更新履歴

参考資料

 

Last-modified: 2023-12-17 (日) 04:19:18