2枚の静止画から顔を変えるアニメーションを作成する【Stylegan2-ada】

顔画像を変化させるアニメーションって作れる?
AIの力使えばできるよ。

今回はStylegan2-adaというものを使って顔画像を変化させるようなアニメーションを作っていきたいと思います。

記事の内容
  • 静止画2枚をStylegan2-adaで動かす

    [StyleGAN2-ADA] 機械学習で顔画像のモーフィング動画を作成 [Python]を参考にしてやっていきますよ。

    顔画像を変化させていきましょう。

    作成するアニメーション

    作成するのは静止画2枚を使ってそれをこんな感じでアニメーションさせたものです。

    これ2枚の画像だけで作られたものなの?
    そうだよ。

    プログラムを使う必要があるけど、GoogleColaboratoryから誰でも使うことができます。

    Stylegan2-adaStlyeGAN2にADAっていう技術を取り込んだものがStylegan2-adaみたいです。ただ正直Githubのページ見てもよく分からないですね。

    人工知能関連のプログラムに詳しい人なら見て分かるんでしょうが、凡人には無理です。

    GoogleColaboratoryからコードを実行する

    GoogleColaboratoryから実行していきます。ランタイムはGPUにして進めましょう。

    !nvidia-smi
    %cd /content
    
    !git clone https://github.com/NVlabs/stylegan2-ada-pytorch.git
    # for align face
    !git clone https://github.com/adamian98/pulse.git

    顔の位置を画像から検出するためにpulseも入れておきます。ライブラリのインストールとインポートを行います。

    !pip install moviepy
    
    # ninja
    !wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip > /dev/null
    !sudo unzip ninja-linux.zip -d /usr/local/bin/ > /dev/null
    !sudo update-alternatives --install /usr/bin/ninja ninja /usr/local/bin/ninja 1 --force > /dev/null
    !pip install imageio==2.4.1
    %cd /content/stylegan2-ada-pytorch
    
    import cv2
    import numpy as np
    from PIL import Image
    import dlib
    from matplotlib import pyplot as plt
    import torch
    import dnnlib
    import legacy
    import imageio
    from tqdm.notebook import tqdm
    
    from moviepy.video.fx.resize import resize
    from moviepy.editor import VideoFileClip

    画像を入れるためのディレクトリを作成します。

    %cd /content/stylegan2-ada-pytorch
    !rm -rf inputs
    !mkdir -p inputs/img inputs/align

    imgっていうフォルダができるので、そこに画像を2枚入れておきます。(ドラッグ&ドロップで可能)

    画像から顔の部分を切り抜くためにpulseのalign_face.pyを使います。

    NETWORK = "https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/ffhq.pkl"
    SOURCE_NAME = '/content/stylegan2-ada-pytorch/src.jpg'
    TARGET_NAME = '/content/stylegan2-ada-pytorch/dst.jpg'
    %cd /content/pulse
    !python align_face.py \
      -input_dir /content/stylegan2-ada-pytorch/inputs/img \
      -output_dir /content/stylegan2-ada-pytorch/inputs/align \
      -output_size 1024 \
      -seed 12

    これでinputsのalignフォルダに顔画像が切り抜かれた画像が配置されました。

    projector.pyを使って画像を投影します。指定するのはさっき切り取った顔の画像です。

    %cd /content/stylegan2-ada-pytorch
    
    !python projector.py \
      --save-video 0 \
      --num-steps 1000 \
      --outdir=outputs/src \
      --target=inputs/align/src_0.png \
      --network={NETWORK}
    %cd /content/stylegan2-ada-pytorch
    
    !python projector.py \
      --save-video 0 \
      --num-steps 1000 \
      --outdir=outputs/dst \
      --target=inputs/align/dst_0.png \
      --network={NETWORK}

    ただ何やっているのかよくわかりません。projector.pyのコード見てもAI関連のプログラミング知識ないから本当に分かりません。

    最後に動画を作成します。

    STEPS = 100
    FPS = 20
    FREEZE_STEPS = 20
    %cd /content/stylegan2-ada-pytorch
    
    lvec1 = np.load('./outputs/src/projected_w.npz')['w']
    lvec2 = np.load('./outputs/dst/projected_w.npz')['w']
    
    network_pkl = "https://nvlabs-fi-cdn.nvidia.com/stylegan2-ada-pytorch/pretrained/ffhq.pkl"
    device = torch.device('cuda')
    with dnnlib.util.open_url(network_pkl) as fp:
        G = legacy.load_network_pkl(fp)['G_ema'].requires_grad_(False).to(device) # type: ignore
    
    diff = lvec2 - lvec1
    step = diff / STEPS
    current = lvec1.copy()
    target_uint8 = np.array([1024,1024,3], dtype=np.uint8)
    
    video = imageio.get_writer('./movie.mp4', mode='I', fps=FPS, codec='libx264', bitrate='16M')
    
    for j in tqdm(range(STEPS)):
      z = torch.from_numpy(current).to(device)
      synth_image = G.synthesis(z, noise_mode='const')
      synth_image = (synth_image + 1) * (255/2)
      synth_image = synth_image.permute(0, 2, 3, 1).clamp(0, 255).to(torch.uint8)[0].cpu().numpy()
    
      repeat = FREEZE_STEPS if j==0 or j==(STEPS-1) else 1
       
      for i in range(repeat):
        video.append_data(synth_image)
      current = current + step
    
    
    video.close()
    clip = VideoFileClip('./movie.mp4')
    clip = resize(clip, height=420)
    clip.ipython_display()

    これで指定した2枚の画像をモーフィングする動画ができました。

    まとめ

    こういうのすごいですよね、画像が2枚あればそれっぽい変化するアニメーションを作れてしまいます。人工知能に詳しい人なら具体的に何をやっているのかっていうことが分かると思うけど、凡人の自分には無理でした。

    (Web開発関連のプログラミング知識と全然違うから苦しいんだよなあ)

    とはいえAIの力は凄いな。