Le deuxième jour de StyleGAN, j'expliquerai deux des trois méthodes de génération d'images avec StyleGAN dans la référence (1) ci-dessous, et j'essaierai diverses générations d'images Style_Mixing. De plus, comme il y a une bonne explication sur StyleGAN dans Reference ②, je pense que l'explication de cet article est facile à comprendre si vous y faites référence. 【référence】 ①NVlabs/stylegan ② Session de lecture du commentaire de StyleGAN CVPR2019 @DeNA
・ Tout d'abord, quelles sont les deux méthodes? ・ Essayez de coder ・ ** Mélange latent **; Essayez de mélanger dans l'espace latent $ z $ ・ ** Mélange de styles **; Essayez de mélanger dans l'espace latent mappé $ w $ ・ ** StyleMixing_2 **; Génère une image en échangeant des attributs de style dans l'espace latent mappé $ w $
Une traduction littérale simple est la suivante.
Il existe trois façons d'utiliser un générateur pré-entraîné: $ 1. Utilisez Gs.run () pour les opérations en mode immédiat où l'entrée et la sortie sont des tableaux numpy. $ ** J'ai utilisé cette technique la dernière fois **
# Pick latent vector.
rnd = np.random.RandomState(5)
latents = rnd.randn(1, Gs.input_shape[1])
# Generate image.
fmt = dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True)
images = Gs.run(latents, None, truncation_psi=0.7, randomize_noise=True, output_transform=fmt)
Le premier argument est un lot de vecteurs latents de forme [num, 512]. Le deuxième argument est réservé aux étiquettes de classe (non utilisé dans StyleGAN) Les arguments de mot-clé restants sont facultatifs et peuvent être utilisés pour modifier davantage l'opération (voir ci-dessous). La sortie est un lot d'images dont le format est déterminé par l'argument output_transform. Veuillez vous référer à Reference ① pour les options (truncation_psi = 0.7, randomize_noise = True) référencées ci-dessous.
src_latents = np.stack(np.random.RandomState(seed).randn(Gs.input_shape[1]) for seed in src_seeds)
src_dlatents = Gs.components.mapping.run(src_latents, None) # [seed, layer, component]
src_images = Gs.components.synthesis.run(src_dlatents, randomize_noise=False, **synthesis_kwargs)
En utilisant la technique ci-dessus, le code le plus simple peut être écrit comme suit:
import os
import pickle
import numpy as np
import PIL.Image
import dnnlib
import dnnlib.tflib as tflib
import config
from PIL import Image, ImageDraw
import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
synthesis_kwargs = dict(output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True), minibatch_size=8)
def main():
# Initialize TensorFlow.
tflib.init_tf()
fpath = './weight_files/tensorflow/karras2019stylegan-ffhq-1024x1024.pkl'
with open(fpath, mode='rb') as f:
_G, _D, Gs = pickle.load(f)
#Méthode 1.Gs pour les opérations en mode immédiat où l'entrée et la sortie sont des tableaux numpy.Utilisez run ()
# Pick latent vector.
rnd = np.random.RandomState(5)
latents1 = rnd.randn(1, Gs.input_shape[1])
# Generate image.
fmt = dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True)
images = Gs.run(latents1, None, truncation_psi=0.7, randomize_noise=True, output_transform=fmt)
plt.imshow(images.reshape(1024,1024,3))
plt.pause(1)
plt.savefig("./results/simple1_.png ")
plt.close()
#Méthode 3.Gs.components.cartographie et Gs.components.Recherche de synthèse pour accéder aux sous-réseaux individuels du générateur
#Comme G, le sous-réseau est dnnlib.tflib.Représenté comme une instance indépendante du réseau.
src_seeds = [5]
src_latents = np.stack(np.random.RandomState(seed).randn(Gs.input_shape[1]) for seed in src_seeds)
src_dlatents = Gs.components.mapping.run(src_latents, None) # [seed, layer, component]
src_images = Gs.components.synthesis.run(src_dlatents, randomize_noise=False, **synthesis_kwargs)
plt.imshow(src_images[0].reshape(1024,1024,3))
plt.pause(1)
plt.savefig("./results/simple3_.png ")
plt.close()
if __name__ == "__main__":
main()
Avec ce code, les deux méthodes semblent générer la même image, mais quand je l'ai essayé, c'était un peu différent comme suit.
Méthode 1 | Méthode 2 | |
---|---|---|
Tenseur latent | z=latents1 | z=src_latents, w=src_dlatents |
size | (1,512) | (1,512), (1,18,512) |
Ces tenseurs latents correspondent respectivement à $ z $ et $ w $ dans la figure ci-dessous. Autrement dit, Latent $ z $ est un vecteur avec 512 paramètres, et son mappage d'espace latent $ W $ tenseur $ w $ a des dimensions (18,512). En d'autres termes, il y a 18 entrées A dans le réseau Synthesis (voir Référence ③), et chacune d'elles est le tenseur $ w $ qui est la base du Style. 【référence】 ③ Essayez le mélange de styles et jouez avec le modèle entraîné de @ StyleGAN En d'autres termes, l'explication des méthodes 1 et 3 ci-dessus peut être reformulée comme suit.
--Méthode 1. L'image est générée à partir du vecteur latent $ z $
C'est la même chose que nous l'avons fait la dernière fois, mais reflétant ce qui précède, nous allons l'implémenter de deux manières pour calculer le vecteur latent $ z $. Le code principal est ci-dessous.
simple_method1.py
def main():
# Initialize TensorFlow.
tflib.init_tf()
fpath = './weight_files/tensorflow/karras2019stylegan-ffhq-1024x1024.pkl'
with open(fpath, mode='rb') as f:
_G, _D, Gs = pickle.load(f)
# Pick latent vector.
rnd = np.random.RandomState(5) #5
latents1 = rnd.randn(1, Gs.input_shape[1])
print(latents1.shape)
# Generate image.
fmt = dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True)
images = Gs.run(latents1, None, truncation_psi=1, randomize_noise=False, output_transform=fmt)
# Pick latent vector2
src_seeds=[6]
src_latents = np.stack(np.random.RandomState(seed).randn(Gs.input_shape[1]) for seed in src_seeds)
# Generate image2
src_dlatents = Gs.components.mapping.run(src_latents, None) # [seed, layer, component]
src_images = Gs.components.synthesis.run(src_dlatents, randomize_noise=False, **synthesis_kwargs)
for i in range(1,101,4):
# mixing latent vetor_1-2
latents = i/100*latents1+(1-i/100)*src_latents[0].reshape(1,512)
# Generate image for mixing vector by method1.
fmt = dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True)
images = Gs.run(latents, None, truncation_psi=1, randomize_noise=False, output_transform=fmt)
# Save image.
os.makedirs(config.result_dir, exist_ok=True)
png_filename = os.path.join(config.result_dir, 'example{}.png'.format(i))
PIL.Image.fromarray(images[0], 'RGB').save(png_filename)
Le résultat est le suivant.
Latent z mixing | |
---|---|
Ici, la sortie des deux était différente dans «Try to code», mais cela était dû aux paramètres de truncation_psi et randomize_noise. Par conséquent, afin d'assurer la reproductibilité, il est changé en 1 et Faux respectivement. Impressions) J'ai peur de voir les visages de mes deux enfants quand je regarde cette vidéo. .. ..
Maintenant, faisons la même chose que ci-dessus, mais avec l'espace latent de mappage $ w $. Les principales parties du code sont:
simple_method2.py
synthesis_kwargs = dict(output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True), minibatch_size=8)
def main():
# Initialize TensorFlow.
tflib.init_tf()
fpath = './weight_files/tensorflow/karras2019stylegan-ffhq-1024x1024.pkl'
with open(fpath, mode='rb') as f:
_G, _D, Gs = pickle.load(f)
# Pick latent vector.
rnd = np.random.RandomState(5) #5
latents1 = rnd.randn(1, Gs.input_shape[1])
# Generate image.
dlatents1 = Gs.components.mapping.run(latents1, None) # [seed, layer, component]
images = Gs.components.synthesis.run(dlatents1, randomize_noise=False, **synthesis_kwargs)
src_seeds=[6]
src_latents = np.stack(np.random.RandomState(seed).randn(Gs.input_shape[1]) for seed in src_seeds)
src_dlatents = Gs.components.mapping.run(src_latents, None) # [seed, layer, component]
src_images = Gs.components.synthesis.run(src_dlatents, randomize_noise=False, **synthesis_kwargs)
for i in range(1,101,4):
dlatents = i/100*dlatents1+(1-i/100)*src_dlatents
# Generate image.
images = Gs.components.synthesis.run(dlatents, randomize_noise=False, **synthesis_kwargs)
# Save image.
os.makedirs(config.result_dir, exist_ok=True)
png_filename = os.path.join(config.result_dir, 'example{}.png'.format(i))
PIL.Image.fromarray(images[0], 'RGB').save(png_filename)
Le résultat est le suivant. À première vue, les résultats sont différents.
Style mixing in projected space | |
---|---|
Il est naturel que l'interpolation linéaire avec le vecteur latent d'entrée $ z $ soit différente de l'interpolation linéaire avec chacun des vecteurs Style $ w $ dans son espace de mappage non linéaire (MLP à plusieurs étages). Autant que je puisse voir, le résultat semble être que l'interpolation linéaire du vecteur Style dans l'espace de cartographie est préférable dans le sens où les lunettes durent plus longtemps. Voyons maintenant que cette interpolation linéaire est encore grossière en termes d'interpolation.
Cette technique est l'exemple le plus célèbre de changement d'image dans le papier. Je vais vous montrer le code immédiatement. Ce code est basé sur le code de la référence ③.
ordinary_style_mixising.py
import os
import pickle
import numpy as np
import PIL.Image
import dnnlib
import dnnlib.tflib as tflib
import config
import matplotlib.pyplot as plt
synthesis_kwargs = dict(output_transform=dict(func=tflib.convert_images_to_uint8, nchw_to_nhwc=True), minibatch_size=8)
def load_Gs():
fpath = './weight_files/tensorflow/karras2019stylegan-ffhq-1024x1024.pkl'
with open(fpath, mode='rb') as f:
_G, _D, Gs = pickle.load(f)
return Gs
def draw_style_mixing_figure(png, Gs, w, h, src_seeds, dst_seeds, style_ranges):
print(png)
src_latents = np.stack(np.random.RandomState(seed).randn(Gs.input_shape[1]) for seed in src_seeds)
src_dlatents = Gs.components.mapping.run(src_latents, None) # [seed, layer, component]
# Pick latent vector.
rnd = np.random.RandomState(5) #5
latents1 = rnd.randn(1, Gs.input_shape[1])
print(latents1.shape)
# Generate image.
dlatents1 = Gs.components.mapping.run(latents1, None) # [seed, layer, component]
images = Gs.components.synthesis.run(dlatents1, randomize_noise=False, **synthesis_kwargs)
dst_dlatents = np.zeros((6,18,512))
for j in range(6):
dst_dlatents[j] = dlatents1
src_images = Gs.components.synthesis.run(src_dlatents, randomize_noise=False, **synthesis_kwargs)
dst_images = Gs.components.synthesis.run(dst_dlatents, randomize_noise=False, **synthesis_kwargs)
print(dst_images.shape)
canvas = PIL.Image.new('RGB', (w * (len(src_seeds) + 1), h * (len(dst_seeds) + 1)), 'white')
for col, src_image in enumerate(list(src_images)):
canvas.paste(PIL.Image.fromarray(src_image, 'RGB'), ((col + 1) * w, 0))
for row, dst_image in enumerate(list(dst_images)):
canvas.paste(PIL.Image.fromarray(dst_image, 'RGB'), (0, (row + 1) * h))
row_dlatents = np.stack([dst_dlatents[row]] * len(src_seeds))
row_dlatents[:, style_ranges[row]] = src_dlatents[:, style_ranges[row]]
row_images = Gs.components.synthesis.run(row_dlatents, randomize_noise=False, **synthesis_kwargs)
for col, image in enumerate(list(row_images)):
canvas.paste(PIL.Image.fromarray(image, 'RGB'), ((col + 1) * w, (row + 1) * h))
canvas.save(png)
def main():
tflib.init_tf()
os.makedirs(config.result_dir, exist_ok=True)
draw_style_mixing_figure(os.path.join(config.result_dir, 'style-mixing.png'),
load_Gs(), w=1024, h=1024, src_seeds=[6,701,687,615,2268], dst_seeds=[0,0,0,0,0,0],
style_ranges=[range(0,8)]+[range(1,8)]+[range(2,8)]+[range(1,18)]+[range(4,18)]+[range(5,18)])
if __name__ == "__main__":
main()
Le résultat est le suivant.
style_ranges=[range(0,8)]+[range(1,8)]+[range(2,8)]+[range(1,18)]+[range(4,18)]+[range(5,18)]
row_dlatents[:, style_ranges[row]] = src_dlatents[:, style_ranges[row]]
En d'autres termes, cette conversion d'image ne peut être effectuée qu'en convertissant les styles suivants à partir du haut de Style [0,18].[range(0,8)] | |
Comme ci-dessus | [range(1,8)] |
Comme ci-dessus | [range(2,8)] |
Comme ci-dessus | [range(1,18)] |
Comme ci-dessus | [range(4,18)] |
Comme ci-dessus | [range(5,18)] |
Au contraire, du point de vue des femmes, en regardant les 2e et 4e étapes, les lunettes sont enlevées simplement parce qu'il n'y a pas de gamme [0], et la féminité se transforme en un sentiment plutôt enfantin. En particulier, la 4ème ligne est la même sauf pour la plage [0], mais il y a des changements considérables.
Voyons donc ce changement en mélangeant le style de cette plage [0] en utilisant le code ci-dessus. Cela peut être réalisé en remplaçant la partie de code appropriée de simple_method2.py par ce qui suit.
individual_mixing_style.py
for i in range(1,26,1):
dlatents=src_dlatents
dlatents[0][0] = i/100*dlatents1[0][0]+(1-i/100)*src_dlatents[0][0]
Individual style mixing in projected space | |
---|---|
Bien qu'elle ne soit pas affichée cette fois, cette méthode vous permet de mélanger des paramètres arbitraires dans l'espace Style, afin que vous puissiez effectuer un mixage plus détaillé.
・ J'ai essayé "une femme qui enlève ses lunettes" ・ Le mélange peut maintenant être effectué en se spécialisant dans chaque caractéristique. ・ Avec cette méthode, la même chose peut être appliquée à l'image donnée par npy.
・ Je veux apprendre ma propre image et générer ma propre image de style
Recommended Posts