1年ぶりにGANで顔を書いてみる

はじめに

1年前に書いたのがこちら。
touch-sp.hatenablog.com
精度がでないので今回はGluonCVのGitHubで公開されているWGANを試してみる。
github.com

環境

Windows10 Pro
NVIDIA GeForce GTX1080
CUDA 10.1
Python 3.6.8

バージョン

absl-py==0.7.1
certifi==2019.6.16
chardet==3.0.4
cycler==0.10.0
gluoncv==0.5.0b20190805
graphviz==0.8.4
grpcio==1.22.0
idna==2.6
kiwisolver==1.1.0
Markdown==3.1.1
matplotlib==3.1.1
mxboard==0.1.0
mxnet-cu101==1.6.0b20190805
numpy==1.16.4
Pillow==6.1.0
protobuf==3.9.1
pyparsing==2.4.2
python-dateutil==2.8.0
requests==2.18.4
scipy==1.3.0
six==1.12.0
tensorboard==1.14.0
tqdm==4.32.2
urllib3==1.22
Werkzeug==0.15.5

データの取得

import os
import tarfile
from mxnet.gluon import utils

#Download the LWF Face Dataset
lfw_url = 'http://vis-www.cs.umass.edu/lfw/lfw-deepfunneled.tgz'
data_path = 'faces'
if not os.path.exists(data_path):
    os.makedirs(data_path)
    data_file = utils.download(lfw_url)
    with tarfile.open(data_file) as f:
        f.extractall(path=data_path)

コードの書き換え

「train_wgan.py」488行目の「for epoch in range(opt.niter):」を「for epoch in range(1, opt.niter+1):」に変更する。
「train_wgan.py」の最後にある「save_params」を「save_parameters」に変更する。
その後実行

python train_wgan.py --dataset lfw --dataroot faces/lfw-deepfunneled --cuda --niter 1000

オリジナル画像

上記学習を実行すると勝手に並べた画像を作ってくれる。
f:id:touch-sp:20190812011019p:plain

結果1

オリジナル画像同様勝手に並べた画像を作ってくれる。

  • 800 epoch

f:id:touch-sp:20190812080032p:plain

  • 1000 epoch

f:id:touch-sp:20190812080113p:plain
1000エポックまでに約3時間かかった。
おそらく1000エポックではまだまだ少ないと思われる。

追加実験1

最初に人物をSemantic Segmentationで切り抜いておくと結果は変わるのか?
実際に試してみた。

import numpy as np
from PIL import Image
import mxnet as mx
from mxnet import image
from mxnet.gluon.data.vision import transforms
import gluoncv

import glob
import os

ctx = mx.gpu()

#データの正規化
transform_fn = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize([.485, .456, .406], [.229, .224, .225])
])

#モデルを読み込む
#初回時に(default)/.mxnet/modelsに保存される
#2回目以降はそこから読み込む
model= gluoncv.model_zoo.get_model('deeplab_resnet152_voc', pretrained=True)
model.collect_params().reset_ctx(ctx)

img_list = glob.glob('wgan/faces/lfw-deepfunneled/*/*.jpg')
out_dir = 'post_faces/face'

for i, filename in enumerate(img_list):

    #画像をNDArryで読み込む
    img = image.imread(filename)

    img = transform_fn(img)
    img = img.expand_dims(0).as_in_context(ctx)

    #モデルの適応
    #人たけを抽出する(class:15)
    output = model.predict(img)
    predict = mx.nd.squeeze(mx.nd.argmax(output, 1)).asnumpy()
    index = np.where(predict !=15)

    #画像を改めて読み込み、結果と重ね合わせる
    img = image.imread(filename).asnumpy()
    img[:,:,0][index] = 255
    img[:,:,1][index] = 255
    img[:,:,2][index] = 255
    save_img = Image.fromarray(img)
    save_filename = os.path.join(out_dir, '%d.jpg'%i)
    save_img.save(save_filename)

結果-追加実験1

  • オリジナル画像

f:id:touch-sp:20190812225536p:plain
一部人物の切り抜きに失敗している(真っ白の画像)。
構わずWGANに突っ込んでみた。

  • 1000 epoch

f:id:touch-sp:20190812225401p:plain
1000エポックまででは大して変わらないと思われる。
ひいき目に見て少し良いような気もする。
さらに学習を進めるとどうなるか?

追加実験2

人物が二人以上写りこんだ写真を除外すれば結果は改善するか?

  • 除外された写真の一部

f:id:touch-sp:20190814160928p:plain
正確性に欠けるが良しとする。
分け方はこちらを参照。

import glob
import random
import mxnet as mx
from mxnet import image
from matplotlib import pyplot as plt 

img_list_all = glob.glob('persons/*.jpg')

random.shuffle(img_list_all)

img_list = img_list_all[0:16]

row = 4
col = 4

array_list = [image.imread(x) for x in img_list]
row_list = []
for i in range(row):
    row_list.append(mx.nd.concat(*array_list[i*col:(i+1)*col], dim = 1))
final = mx.nd.concat(*row_list, dim=0)
plt.imshow(final.asnumpy())
plt.axis('off')
plt.show()

結果-追加実験2

  • オリジナル画像

f:id:touch-sp:20190818123349p:plain

  • 1000 epoch

f:id:touch-sp:20190818123607p:plain

追加実験3

「追加実験2」→「追加実験1」の順で前処理を行ってみた。

結果-追加実験3

  • オリジナル画像

f:id:touch-sp:20190818165756p:plain

  • 2000 epoch

f:id:touch-sp:20190818165834p:plain

  • 4000 epoch

f:id:touch-sp:20190818215843p:plain
物体検出とセグメンテーションを1つのネットワークで同時におこなったのがこちら