<reponame>leonardocarboni/cubismGAN
from dis import dis
from keras.layers import Input, Reshape, Dropout, Dense, Flatten, BatchNormalization, Activation, ZeroPadding2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D, Conv2D
from keras.models import Sequential, Model, load_model
from keras.optimizers import Adam
import numpy as np
from PIL import Image
import os

# MACROS
# Preview image Frame
PREVIEW_ROWS = 4
PREVIEW_COLS = 7
PREVIEW_MARGIN = 4
SAVE_FREQ = 100

# Size vector to generate images from
NOISE_SIZE = 100

# Configuration
EPOCHS = 10000 # number of iterations
BATCH_SIZE = 32
GENERATE_RES = 3
IMAGE_SIZE = 128 # rows/cols
IMAGE_CHANNELS = 3

DATA_FILE = 'cubism_data.npy'
training_data = np.load(DATA_FILE)


def build_discriminator(image_shape):
    # Sequential Model
    model = Sequential()

    # LAYER 1
    # convolutional layer of 32 shape having kernel_size of 3 
    # and stride value is 2 with no additional padding
    # [Lookup on Keras]
    model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=image_shape, padding="same"))
    # Activation Function
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 2
    model.add(Conv2D(64, kernel_size=3, strides=2, padding="same"))
    model.add(ZeroPadding2D(padding=((0, 1), (0, 1))))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 3
    model.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 4
    model.add(Conv2D(256, kernel_size=3, strides=1, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 5
    model.add(Conv2D(512, kernel_size=3, strides=1, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # Decide if it's real or fake with sigmoid function
    model.add(Flatten())
    model.add(Dense(1, activation='sigmoid'))
    input_image = Input(shape=image_shape)
    validity = model(input_image)

    return Model(input_image, validity)

def build_generator(noise_size, channels):
    model = Sequential()
    
    # Fully Dense Layer of size = 4096 
    model.add(Dense(4 * 4 * 256, activation="relu", input_dim=noise_size))
    # Reshaping the layer to a real 4X4X256
    model.add(Reshape((4, 4, 256)))
    model.add(UpSampling2D())

    model.add(Conv2D(256, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu"))
    model.add(UpSampling2D())

    model.add(Conv2D(256, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu"))

    for i in range(GENERATE_RES):
         model.add(UpSampling2D())
         model.add(Conv2D(256, kernel_size=3, padding="same"))
         model.add(BatchNormalization(momentum=0.8))
         model.add(Activation("relu"))

    model.summary()
    model.add(Conv2D(channels, kernel_size=3, padding="same"))
    model.add(Activation("tanh"))

    input = Input(shape=(noise_size,))
    generated_image = model(input)
    
    return Model(input, generated_image)

def save_images(cnt, noise):
    image_array = np.full((
        PREVIEW_MARGIN + (PREVIEW_ROWS * (IMAGE_SIZE + PREVIEW_MARGIN)),
        PREVIEW_MARGIN + (PREVIEW_COLS * (IMAGE_SIZE + PREVIEW_MARGIN)), 3),
        255, dtype=np.uint8)

    generated_images = generator.predict(noise)
    generated_images = 0.5 * generated_images + 0.5
    image_count = 0
    for row in range(PREVIEW_ROWS):
        for col in range(PREVIEW_COLS):
            r = row * (IMAGE_SIZE + PREVIEW_MARGIN) + PREVIEW_MARGIN
            c = col * (IMAGE_SIZE + PREVIEW_MARGIN) + PREVIEW_MARGIN
            image_array[r:r + IMAGE_SIZE, c:c +
                        IMAGE_SIZE] = generated_images[image_count] * 255
            image_count += 1

    output_path = 'output'
    if not os.path.exists(output_path):
        os.makedirs(output_path)
    filename = os.path.join(output_path, f"trained-{cnt}.png")
    im = Image.fromarray(image_array)
    im.save(filename)


# Models Compiling and Training

image_shape = (IMAGE_SIZE, IMAGE_SIZE, IMAGE_CHANNELS)

# Optimizer (Adam [Lookup on Keras])
optimizer = Adam(1.5e-4, 0.5)

# Discriminator (compiled with a loss function and the optimizer,
# using accuracy as metric because it is a classification model)
discriminator = build_discriminator(image_shape)
discriminator.compile(loss="binary_crossentropy", optimizer=optimizer, metrics=["accuracy"])

# Generator
generator = build_generator(NOISE_SIZE, IMAGE_CHANNELS)

# Creating a random input for the generator
random_input = Input(shape=(NOISE_SIZE,))

# Image generated by random input
generated_image = generator(random_input)

# Setting the discrimintaor as untrainable, only training the generator
# by not doing this the gerator will learn how to trick the discriminator
# and the result won't be optimal
discriminator.trainable = False

validity = discriminator(generated_image)

# Combined model
combined = Model(random_input, validity)
combined.compile(loss="binary_crossentropy", optimizer=optimizer, metrics=["accuracy"])

# all ones and zeros vectors
y_real = np.ones((BATCH_SIZE, 1))
y_fake = np.zeros((BATCH_SIZE, 1))

# fixed noise value.
fixed_noise = np.random.normal(0, 1, (PREVIEW_ROWS * PREVIEW_COLS, NOISE_SIZE))

# Iteration over the training data
cnt = 1
for epoch in range(EPOCHS):
    # taking a sample from the real image
    idx = np.random.randint(0, training_data.shape[0], BATCH_SIZE)
    x_real = training_data[idx]

    # generating noise and passing it to a fake vector
    noise= np.random.normal(0, 1, (BATCH_SIZE, NOISE_SIZE))
    x_fake = generator.predict(noise)
 
    # training the discriminator in real and fake (generated) data separetly
    discriminator_metric_real = discriminator.train_on_batch(x_real, y_real)
    discriminator_metric_generated = discriminator.train_on_batch(x_fake, y_fake)
 
    # average metric between real and generated data
    discriminator_metric = 0.5 * np.add(discriminator_metric_real, discriminator_metric_generated)

    # training the generator on real data and selected noise value
    generator_metric = combined.train_on_batch(noise, y_real)

    # every EPOCHS/SAVE_FREQ iterations we save the image and print the data at that checkpoint
    if epoch % SAVE_FREQ == 0:
        save_images(cnt, fixed_noise)
        cnt += 1 
        print(f"{epoch} epoch, Discriminator accuracy: {100*  discriminator_metric[1]}, Generator accuracy: {100 * generator_metric[1]}")
from dis import dis
from keras.layers import Input, Reshape, Dropout, Dense, Flatten, BatchNormalization, Activation, ZeroPadding2D
from keras.layers.advanced_activations import LeakyReLU
from keras.layers.convolutional import UpSampling2D, Conv2D
from keras.models import Sequential, Model, load_model
from keras.optimizers import Adam
import numpy as np
from PIL import Image
import os

# MACROS
# Preview image Frame
PREVIEW_ROWS = 4
PREVIEW_COLS = 7
PREVIEW_MARGIN = 4
SAVE_FREQ = 100

# Size vector to generate images from
NOISE_SIZE = 100

# Configuration
EPOCHS = 10000 # number of iterations
BATCH_SIZE = 32
GENERATE_RES = 3
IMAGE_SIZE = 128 # rows/cols
IMAGE_CHANNELS = 3

DATA_FILE = 'cubism_data.npy'
training_data = np.load(DATA_FILE)


def build_discriminator(image_shape):
    # Sequential Model
    model = Sequential()

    # LAYER 1
    # convolutional layer of 32 shape having kernel_size of 3 
    # and stride value is 2 with no additional padding
    # [Lookup on Keras]
    model.add(Conv2D(32, kernel_size=3, strides=2, input_shape=image_shape, padding="same"))
    # Activation Function
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 2
    model.add(Conv2D(64, kernel_size=3, strides=2, padding="same"))
    model.add(ZeroPadding2D(padding=((0, 1), (0, 1))))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 3
    model.add(Conv2D(128, kernel_size=3, strides=2, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 4
    model.add(Conv2D(256, kernel_size=3, strides=1, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # LAYER 5
    model.add(Conv2D(512, kernel_size=3, strides=1, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(LeakyReLU(alpha=0.2))
    model.add(Dropout(0.25))

    # Decide if it's real or fake with sigmoid function
    model.add(Flatten())
    model.add(Dense(1, activation='sigmoid'))
    input_image = Input(shape=image_shape)
    validity = model(input_image)

    return Model(input_image, validity)

def build_generator(noise_size, channels):
    model = Sequential()
    
    # Fully Dense Layer of size = 4096 
    model.add(Dense(4 * 4 * 256, activation="relu", input_dim=noise_size))
    # Reshaping the layer to a real 4X4X256
    model.add(Reshape((4, 4, 256)))
    model.add(UpSampling2D())

    model.add(Conv2D(256, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu"))
    model.add(UpSampling2D())

    model.add(Conv2D(256, kernel_size=3, padding="same"))
    model.add(BatchNormalization(momentum=0.8))
    model.add(Activation("relu"))

    for i in range(GENERATE_RES):
         model.add(UpSampling2D())
         model.add(Conv2D(256, kernel_size=3, padding="same"))
         model.add(BatchNormalization(momentum=0.8))
         model.add(Activation("relu"))

    model.summary()
    model.add(Conv2D(channels, kernel_size=3, padding="same"))
    model.add(Activation("tanh"))

    input = Input(shape=(noise_size,))
    generated_image = model(input)
    
    return Model(input, generated_image)

def save_images(cnt, noise):
    image_array = np.full((
        PREVIEW_MARGIN + (PREVIEW_ROWS * (IMAGE_SIZE + PREVIEW_MARGIN)),
        PREVIEW_MARGIN + (PREVIEW_COLS * (IMAGE_SIZE + PREVIEW_MARGIN)), 3),
        255, dtype=np.uint8)

    generated_images = generator.predict(noise)
    generated_images = 0.5 * generated_images + 0.5
    image_count = 0
    for row in range(PREVIEW_ROWS):
        for col in range(PREVIEW_COLS):
            r = row * (IMAGE_SIZE + PREVIEW_MARGIN) + PREVIEW_MARGIN
            c = col * (IMAGE_SIZE + PREVIEW_MARGIN) + PREVIEW_MARGIN
            image_array[r:r + IMAGE_SIZE, c:c +
                        IMAGE_SIZE] = generated_images[image_count] * 255
            image_count += 1

    output_path = 'output'
    if not os.path.exists(output_path):
        os.makedirs(output_path)
    filename = os.path.join(output_path, f"trained-{cnt}.png")
    im = Image.fromarray(image_array)
    im.save(filename)


# Models Compiling and Training

image_shape = (IMAGE_SIZE, IMAGE_SIZE, IMAGE_CHANNELS)

# Optimizer (Adam [Lookup on Keras])
optimizer = Adam(1.5e-4, 0.5)

# Discriminator (compiled with a loss function and the optimizer,
# using accuracy as metric because it is a classification model)
discriminator = build_discriminator(image_shape)
discriminator.compile(loss="binary_crossentropy", optimizer=optimizer, metrics=["accuracy"])

# Generator
generator = build_generator(NOISE_SIZE, IMAGE_CHANNELS)

# Creating a random input for the generator
random_input = Input(shape=(NOISE_SIZE,))

# Image generated by random input
generated_image = generator(random_input)

# Setting the discrimintaor as untrainable, only training the generator
# by not doing this the gerator will learn how to trick the discriminator
# and the result won't be optimal
discriminator.trainable = False

validity = discriminator(generated_image)

# Combined model
combined = Model(random_input, validity)
combined.compile(loss="binary_crossentropy", optimizer=optimizer, metrics=["accuracy"])

# all ones and zeros vectors
y_real = np.ones((BATCH_SIZE, 1))
y_fake = np.zeros((BATCH_SIZE, 1))

# fixed noise value.
fixed_noise = np.random.normal(0, 1, (PREVIEW_ROWS * PREVIEW_COLS, NOISE_SIZE))

# Iteration over the training data
cnt = 1
for epoch in range(EPOCHS):
    # taking a sample from the real image
    idx = np.random.randint(0, training_data.shape[0], BATCH_SIZE)
    x_real = training_data[idx]

    # generating noise and passing it to a fake vector
    noise= np.random.normal(0, 1, (BATCH_SIZE, NOISE_SIZE))
    x_fake = generator.predict(noise)
 
    # training the discriminator in real and fake (generated) data separetly
    discriminator_metric_real = discriminator.train_on_batch(x_real, y_real)
    discriminator_metric_generated = discriminator.train_on_batch(x_fake, y_fake)
 
    # average metric between real and generated data
    discriminator_metric = 0.5 * np.add(discriminator_metric_real, discriminator_metric_generated)

    # training the generator on real data and selected noise value
    generator_metric = combined.train_on_batch(noise, y_real)

    # every EPOCHS/SAVE_FREQ iterations we save the image and print the data at that checkpoint
    if epoch % SAVE_FREQ == 0:
        save_images(cnt, fixed_noise)
        cnt += 1 
        print(f"{epoch} epoch, Discriminator accuracy: {100*  discriminator_metric[1]}, Generator accuracy: {100 * generator_metric[1]}")
# MACROS # Preview image Frame # Size vector to generate images from # Configuration # number of iterations # rows/cols # Sequential Model # LAYER 1 # convolutional layer of 32 shape having kernel_size of 3 # and stride value is 2 with no additional padding # [Lookup on Keras] # Activation Function # LAYER 2 # LAYER 3 # LAYER 4 # LAYER 5 # Decide if it's real or fake with sigmoid function # Fully Dense Layer of size = 4096 # Reshaping the layer to a real 4X4X256 # Models Compiling and Training # Optimizer (Adam [Lookup on Keras]) # Discriminator (compiled with a loss function and the optimizer, # using accuracy as metric because it is a classification model) # Generator # Creating a random input for the generator # Image generated by random input # Setting the discrimintaor as untrainable, only training the generator # by not doing this the gerator will learn how to trick the discriminator # and the result won't be optimal # Combined model # all ones and zeros vectors # fixed noise value. # Iteration over the training data # taking a sample from the real image # generating noise and passing it to a fake vector # training the discriminator in real and fake (generated) data separetly # average metric between real and generated data # training the generator on real data and selected noise value # every EPOCHS/SAVE_FREQ iterations we save the image and print the data at that checkpoint