[PYTHON] [Introduction à StyleGAN] J'ai joué avec style_mixing "Femme qui enlève ses lunettes" ♬

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/styleganSession de lecture du commentaire de StyleGAN CVPR2019 @DeNA

Ce que j'ai fait

・ 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 $

・ Tout d'abord, quelles sont les deux méthodes?

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.

2.Use Gs.get_output_for() to incorporate the generator as a part of a larger TensorFlow expression: Je vais sauter ceci car je ne l'utiliserai pas cette fois. ... Recherchez $ 3.Gs.components.mapping et Gs.components.synthesis $ pour accéder aux sous-réseaux individuels du générateur. Comme $ G $, les sous-réseaux sont représentés comme des instances indépendantes de $ dnnlib.tflib.Network $. :: ** Cette fois, nous utiliserons cette technique dans l'image générée Mixing **

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)

・ Essayez de coder

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)
simple1_.png simple3_.png

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 styleGAN_fig1.jpg 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 $

・ Mélange latent; Essayez de mélanger dans l'espace 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 simple3_.png
simple1_.png simple_method10_1_256.gif

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. .. ..

・ StyleMixing; Essayez de mélanger dans l'espace latent mappé w

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 simple3_.png
simple1_.png simple_method10_1.gif

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.

・ StyleMixing_2; Générer une image en échangeant des attributs de style dans l'espace latent mappé w

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.

simple3_.png
simple1_.png [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.

-StyleMixing_3; Mélange de l'attribut Style individuel de l'espace latent de mappage w pour générer une image

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 simple3_.png
simple1_.png simple_method10_1_512.gif

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é.

Résumé

・ 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

[Introduction à StyleGAN] J'ai joué avec style_mixing "Femme qui enlève ses lunettes" ♬
[Introduction à StyleGAN] J'ai joué avec "Une femme se transforme en Mayuyu" ♬
[Introduction à Pytorch] J'ai joué avec sinGAN ♬
[Introduction à StyleGAN] J'ai joué avec "The Life of a Man" ♬
[Introduction à sinGAN-Tensorflow] J'ai joué avec la super-résolution "Challenge Big Imayuyu" ♬
[Introduction à Matplotlib] Axes Animation 3D: J'ai joué avec des figurines 3D Lisaju ♬
[Introduction à RasPi4] J'ai joué avec "la conversation de langue empoisonnée d'Hiroko / Hiromi" ♪
[Introduction à AWS] J'ai joué avec des voix masculines et féminines avec Polly et Transcribe ♪
[Introduction à StyleGAN2] Apprentissage indépendant avec 10 visages d'anime ♬
[Introduction au trading système] J'ai dessiné un oscillateur stochastique avec python et joué avec ♬
[Introduction à Pytorch] J'ai essayé de catégoriser Cifar10 avec VGG16 ♬
[Introduction à AWS] J'ai essayé de jouer avec la conversion voix-texte ♪
J'ai joué avec wordcloud!
[Introduction à StyleGAN] Cet "homme qui ne rit pas" a souri sans le vouloir ♬
Introduction à RDB avec sqlalchemy Ⅰ
Introduction à l'optimisation non linéaire (I)
J'ai lu "Renforcer l'apprentissage avec Python de l'introduction à la pratique" Chapitre 1
[Introduction] Je veux créer un robot Mastodon avec Python! 【Débutants】
[Introduction au style GAN] Apprentissage unique de l'animation avec votre propre machine ♬
J'ai lu "Renforcer l'apprentissage avec Python de l'introduction à la pratique" Chapitre 2