Spaces:
Runtime error
Runtime error
LuisDarioHinojosa commited on
Commit ·
5fe4fb2
1
Parent(s): 4a2c19b
initial commit
Browse files- README.md +1 -1
- app.py +107 -0
- examples/content/P1011143.jpg +0 -0
- examples/content/SECRETOS-CATEDRAL-METROPOLITANA-CDMX-2.jpg +0 -0
- examples/content/_129303416_gettyimages-992816562-1.jpg +0 -0
- examples/content/b23764853d02ad4c25e0780c2cd6aa52--volcano-los.jpg +0 -0
- examples/content/f.elconfidencial.com_original_dae_7b7_861_dae7b7861c3285b03b9f6555878c8c53.jpg +0 -0
- examples/style/1200px-The_Fighting_Temeraire,_JMW_Turner,_National_Gallery.jpg +0 -0
- examples/style/Giotto-Di-Bondone-The-Ascension.jpg +0 -0
- examples/style/fenmeno-de-ingravidez.jpg +0 -0
- examples/style/ou_nous_allons.jpg +0 -0
- examples/style/starry-night-over-the-rhone-5.jpg +0 -0
- modules/nst_loss_functions.py +68 -0
- modules/nst_models.py +33 -0
- modules/preprocessing_utils.py +31 -0
- requirements.txt +4 -0
README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
| 1 |
---
|
| 2 |
title: Neural Style Transfer Demo
|
| 3 |
-
emoji:
|
| 4 |
colorFrom: purple
|
| 5 |
colorTo: red
|
| 6 |
sdk: gradio
|
|
|
|
| 1 |
---
|
| 2 |
title: Neural Style Transfer Demo
|
| 3 |
+
emoji: 🎨
|
| 4 |
colorFrom: purple
|
| 5 |
colorTo: red
|
| 6 |
sdk: gradio
|
app.py
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import numpy as np
|
| 3 |
+
import tensorflow as tf
|
| 4 |
+
import gradio as gr
|
| 5 |
+
import random
|
| 6 |
+
from PIL import Image
|
| 7 |
+
from tensorflow.keras.preprocessing.image import load_img,img_to_array
|
| 8 |
+
from tensorflow.keras.optimizers import SGD
|
| 9 |
+
from tensorflow.keras.optimizers.schedules import ExponentialDecay
|
| 10 |
+
# change in the actual written file
|
| 11 |
+
from modules.nst_loss_functions import *
|
| 12 |
+
from modules.nst_models import *
|
| 13 |
+
from modules.preprocessing_utils import *
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# get the weights
|
| 17 |
+
content_weight,style_weight = get_weights()
|
| 18 |
+
# get the content and style layer lists
|
| 19 |
+
content_layer,style_layers = get_layers_lists()
|
| 20 |
+
# instance the pretrained model
|
| 21 |
+
pretrained_vgg_model,feature_extractor = get_pretrained_vgg_model_fe()
|
| 22 |
+
|
| 23 |
+
# training function
|
| 24 |
+
@tf.function
|
| 25 |
+
def compute_loss_and_grads(generated_image,
|
| 26 |
+
base_image,
|
| 27 |
+
style_image,
|
| 28 |
+
row_cols):
|
| 29 |
+
|
| 30 |
+
with tf.GradientTape() as tape:
|
| 31 |
+
loss = loss_function(generated_image = generated_image,
|
| 32 |
+
base_image = base_image,
|
| 33 |
+
style_image = style_image,
|
| 34 |
+
content_layer = content_layer,
|
| 35 |
+
style_layers = style_layers,
|
| 36 |
+
feature_extractor = feature_extractor,
|
| 37 |
+
weights= (content_weight,style_weight),
|
| 38 |
+
rows_cols = row_cols)
|
| 39 |
+
|
| 40 |
+
grads = tape.gradient(loss, generated_image)
|
| 41 |
+
return loss, grads
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# generate image
|
| 45 |
+
def generate(base_image,style_image,epochs,progress=gr.Progress()):
|
| 46 |
+
# instance images
|
| 47 |
+
base_pil_image = base_image
|
| 48 |
+
style_pil_image = style_image
|
| 49 |
+
generated_pil_image = base_pil_image.copy()
|
| 50 |
+
|
| 51 |
+
# determine the base image's dimentions
|
| 52 |
+
width, height = base_pil_image.size
|
| 53 |
+
img_nrows = 400
|
| 54 |
+
img_ncols = int(width * img_nrows / height)
|
| 55 |
+
|
| 56 |
+
# instance the optimizer
|
| 57 |
+
optimizer = SGD(
|
| 58 |
+
ExponentialDecay(
|
| 59 |
+
initial_learning_rate=100.0, decay_steps=100, decay_rate=0.96
|
| 60 |
+
)
|
| 61 |
+
)
|
| 62 |
+
|
| 63 |
+
# preprocess the images
|
| 64 |
+
base_image = preprocess_image(base_pil_image,(img_nrows,img_ncols))
|
| 65 |
+
style_image = preprocess_image(style_pil_image,(img_nrows,img_ncols))
|
| 66 |
+
generated_image = tf.Variable(preprocess_image(generated_pil_image,(img_nrows,img_ncols)))
|
| 67 |
+
|
| 68 |
+
for i in progress.tqdm(range(int(epochs))):
|
| 69 |
+
loss, grads = compute_loss_and_grads(
|
| 70 |
+
generated_image, base_image, style_image,(img_nrows,img_ncols)
|
| 71 |
+
)
|
| 72 |
+
optimizer.apply_gradients([(grads, generated_image)])
|
| 73 |
+
|
| 74 |
+
generated_image = generated_image.numpy()
|
| 75 |
+
generated_image = deprocess_image(generated_image,(img_nrows,img_ncols))
|
| 76 |
+
|
| 77 |
+
return generated_image
|
| 78 |
+
|
| 79 |
+
title = "Neural Style Transfer Demo"
|
| 80 |
+
description = "This is my implementation of the neural style transfer algorithm using Tensorflow2"
|
| 81 |
+
article = "The NST algorithm is an algorithm that allows you to replicate an image A with similar features to the ones present in an image B. In a nutshell, this is done by using a pretrained CNN to perform gradient descent on the weighted cost of a style and content cost function, which correspond to the frobenius norm across the features’ cross covariance across different layers and the simple norm respectively. The result of the loss is applied to a random generated image to get the hybrid. To use this app, select a real photo as a content image and an art piece as style image from an URL or from your PC, set the number of epochs (it is recommended to leave the default value), and run the app. THIS MAY TAKE SOME TIME, PLEASE BE PATIENT (╯°□°)╯."
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
example_list = list()
|
| 86 |
+
# change in the app
|
| 87 |
+
examples_path = "examples"
|
| 88 |
+
content_examples_path = os.path.join(examples_path,"content")
|
| 89 |
+
style_examples_path = os.path.join(examples_path,"style")
|
| 90 |
+
|
| 91 |
+
content_examples = [[str(content_examples_path) + "/" + example] for example in os.listdir(content_examples_path)]
|
| 92 |
+
style_examples = [[str(style_examples_path) + "/" + example] for example in os.listdir(style_examples_path)]
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
img_input_1 = gr.Image(label = "Content Image",type = "pil",value = random.choice(content_examples)[0])
|
| 96 |
+
img_input_2 = gr.Image(label = "Style Image",type = "pil",value = random.choice(style_examples)[0])
|
| 97 |
+
|
| 98 |
+
demo = gr.Interface(
|
| 99 |
+
fn = generate,
|
| 100 |
+
inputs = [img_input_1,img_input_2,gr.Number(value = 250,label = "Number of epochs",)],
|
| 101 |
+
outputs = [gr.Image(type = "pil")],
|
| 102 |
+
title = title,
|
| 103 |
+
description = description,
|
| 104 |
+
article = article
|
| 105 |
+
)
|
| 106 |
+
|
| 107 |
+
demo.queue().launch()
|
examples/content/P1011143.jpg
ADDED
|
examples/content/SECRETOS-CATEDRAL-METROPOLITANA-CDMX-2.jpg
ADDED
|
examples/content/_129303416_gettyimages-992816562-1.jpg
ADDED
|
examples/content/b23764853d02ad4c25e0780c2cd6aa52--volcano-los.jpg
ADDED
|
examples/content/f.elconfidencial.com_original_dae_7b7_861_dae7b7861c3285b03b9f6555878c8c53.jpg
ADDED
|
examples/style/1200px-The_Fighting_Temeraire,_JMW_Turner,_National_Gallery.jpg
ADDED
|
examples/style/Giotto-Di-Bondone-The-Ascension.jpg
ADDED
|
examples/style/fenmeno-de-ingravidez.jpg
ADDED
|
examples/style/ou_nous_allons.jpg
ADDED
|
examples/style/starry-night-over-the-rhone-5.jpg
ADDED
|
modules/nst_loss_functions.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import tensorflow as tf
|
| 3 |
+
|
| 4 |
+
# compute the gram matrix of the features
|
| 5 |
+
def gram_matrix(x):
|
| 6 |
+
#x = tf.convert_to_tensor(x, tf.int32)
|
| 7 |
+
x = tf.transpose(x,perm = (2,0,1))
|
| 8 |
+
features = tf.reshape(x,(tf.shape(x)[0],-1))
|
| 9 |
+
gram = tf.matmul(features,tf.transpose(features))
|
| 10 |
+
return gram
|
| 11 |
+
|
| 12 |
+
# compute the style cost function
|
| 13 |
+
def style_cost_function(style_image,generated_image,rows_cols):
|
| 14 |
+
img_nrows,img_ncols = rows_cols
|
| 15 |
+
S = gram_matrix(style_image)
|
| 16 |
+
C = gram_matrix(generated_image)
|
| 17 |
+
channels = 3
|
| 18 |
+
size = img_nrows * img_ncols
|
| 19 |
+
return tf.reduce_sum(tf.square(S - C)) / (4.0 * (channels ** 2) * (size ** 2))
|
| 20 |
+
|
| 21 |
+
# compute the content cost function
|
| 22 |
+
def content_cost_function(base_image,generated_image):
|
| 23 |
+
return tf.reduce_sum(tf.square(tf.subtract(generated_image,base_image)))
|
| 24 |
+
|
| 25 |
+
def loss_function(
|
| 26 |
+
generated_image,
|
| 27 |
+
base_image,
|
| 28 |
+
style_image,
|
| 29 |
+
content_layer,
|
| 30 |
+
style_layers,
|
| 31 |
+
feature_extractor,
|
| 32 |
+
weights,
|
| 33 |
+
rows_cols):
|
| 34 |
+
|
| 35 |
+
# fetch the weights
|
| 36 |
+
content_weight,style_weight = weights
|
| 37 |
+
|
| 38 |
+
# combine the images into a single tensor
|
| 39 |
+
input_tensor = tf.concat(
|
| 40 |
+
[base_image, style_image, generated_image], axis=0
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
# get the values in all the layers for the three images
|
| 44 |
+
features = feature_extractor(input_tensor)
|
| 45 |
+
|
| 46 |
+
# iinitialize the loss function
|
| 47 |
+
loss = tf.zeros(shape=())
|
| 48 |
+
|
| 49 |
+
# compute the content loss
|
| 50 |
+
layer_features = features[content_layer]
|
| 51 |
+
base_image_features = layer_features[0, :, :, :]
|
| 52 |
+
combination_features = layer_features[2, :, :, :]
|
| 53 |
+
|
| 54 |
+
loss = loss + content_weight * content_cost_function(
|
| 55 |
+
base_image_features, combination_features
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
# compute the style loss
|
| 59 |
+
for layer_name in style_layers:
|
| 60 |
+
layer_features = features[layer_name]
|
| 61 |
+
style_reference_features = layer_features[1, :, :, :]
|
| 62 |
+
combination_features = layer_features[2, :, :, :]
|
| 63 |
+
sl = style_cost_function(style_reference_features, combination_features,rows_cols)
|
| 64 |
+
loss += (style_weight / len(style_layers)) * sl
|
| 65 |
+
|
| 66 |
+
return loss
|
| 67 |
+
|
| 68 |
+
|
modules/nst_models.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from tensorflow.keras.models import Model
|
| 2 |
+
from tensorflow.keras.applications import VGG19
|
| 3 |
+
|
| 4 |
+
# instances a pretrained vgg19 model and creates the feature extractor
|
| 5 |
+
def get_pretrained_vgg_model_fe():
|
| 6 |
+
# instance the pretrained model
|
| 7 |
+
pretrained_vgg_model = VGG19(include_top = False, weights="imagenet")
|
| 8 |
+
# create a dictionary that maps the layers name to each feature extractor
|
| 9 |
+
model_outputs = {layer.name : layer.output for layer in pretrained_vgg_model.layers}
|
| 10 |
+
# feature extractor
|
| 11 |
+
feature_extractor = Model(inputs=pretrained_vgg_model.inputs, outputs=model_outputs)
|
| 12 |
+
return pretrained_vgg_model,feature_extractor
|
| 13 |
+
|
| 14 |
+
# get a list of the layers that will be used to style and content
|
| 15 |
+
def get_layers_lists():
|
| 16 |
+
# define the style layers
|
| 17 |
+
style_layers = [
|
| 18 |
+
"block1_conv1",
|
| 19 |
+
"block2_conv1",
|
| 20 |
+
"block3_conv1",
|
| 21 |
+
"block4_conv1",
|
| 22 |
+
"block5_conv1",
|
| 23 |
+
]
|
| 24 |
+
# define the content layer
|
| 25 |
+
content_layer = "block5_conv2"
|
| 26 |
+
return content_layer,style_layers
|
| 27 |
+
|
| 28 |
+
# get the weights for each of the cost functions in order to computer the final cost
|
| 29 |
+
def get_weights():
|
| 30 |
+
# define the style and content weights
|
| 31 |
+
content_weight = 2.5e-8
|
| 32 |
+
style_weight = 1.0e-6
|
| 33 |
+
return content_weight,style_weight
|
modules/preprocessing_utils.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import tensorflow as tf
|
| 3 |
+
from tensorflow.keras.applications.vgg19 import preprocess_input
|
| 4 |
+
from tensorflow.keras.preprocessing.image import img_to_array
|
| 5 |
+
|
| 6 |
+
def preprocess_image(img,rows_cols):
|
| 7 |
+
img_nrows, img_ncols = rows_cols
|
| 8 |
+
# load the image into a tensot
|
| 9 |
+
img = img.resize((img_ncols,img_nrows))
|
| 10 |
+
# turn the image into a numpy array
|
| 11 |
+
img = img_to_array(img)
|
| 12 |
+
# add a batch dimention
|
| 13 |
+
img = np.expand_dims(img, axis=0)
|
| 14 |
+
# preprocess according to the vgg model's specification
|
| 15 |
+
img = preprocess_input(img)
|
| 16 |
+
return tf.convert_to_tensor(img)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def deprocess_image(x,rows_cols):
|
| 20 |
+
img_nrows, img_ncols = rows_cols
|
| 21 |
+
# Cconert to array
|
| 22 |
+
x = x.reshape((img_nrows, img_ncols, 3))
|
| 23 |
+
# mean = 0
|
| 24 |
+
x[:, :, 0] += 103.939
|
| 25 |
+
x[:, :, 1] += 116.779
|
| 26 |
+
x[:, :, 2] += 123.68
|
| 27 |
+
# convert to rgb
|
| 28 |
+
x = x[:, :, ::-1]
|
| 29 |
+
# normalize
|
| 30 |
+
x = np.clip(x, 0, 255).astype("uint8")
|
| 31 |
+
return x
|
requirements.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
tensorflow==2.12.0
|
| 2 |
+
gradio==3.34.0
|
| 3 |
+
numpy==1.22.4
|
| 4 |
+
Pillow==8.4.0
|