Style-transfer / app.py
bhavyapandya's picture
Upload 2 files
73f71ed
raw
history blame
6.18 kB
import gradio as gr
import tensorflow as tf
from PIL import Image
from tensorflow.keras.applications import vgg19
from tensorflow.python.keras import models
import numpy as np
from tensorflow import keras
from werkzeug.utils import secure_filename
import os
from flask import send_file
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
img_nrows = 256
img_ncols = 300
def img_preprocess(image_path):
# Util function to open, resize and format pictures into appropriate tensors
# img = keras.preprocessing.image.load_img(
# image_path, target_size=(img_nrows, img_ncols)
# )
img = keras.preprocessing.image.img_to_array(image_path)
img = np.expand_dims(img, axis=0)
img = vgg19.preprocess_input(img)
return tf.convert_to_tensor(img)
def deprocess_img(processed_img):
x = processed_img.copy()
if len(x.shape) == 4:
x = np.squeeze(x, 0)
assert len(x.shape) == 3 #Input dimension must be [1, height, width, channel] or [height, width, channel]
# perform the inverse of the preprocessing step
x[:, :, 0] += 103.939
x[:, :, 1] += 116.779
x[:, :, 2] += 123.68
x = x[:, :, ::-1] # converting BGR to RGB channel
x = np.clip(x, 0, 255).astype('uint8')
return x
content_layers = ['block5_conv2']
style_layers = ['block1_conv1',
'block2_conv1',
'block3_conv1',
'block4_conv1',
'block5_conv1']
number_content=len(content_layers)
number_style =len(style_layers)
def get_model():
vgg=tf.keras.applications.vgg19.VGG19(include_top=False,weights='imagenet')
vgg.trainable=False
content_output=[vgg.get_layer(layer).output for layer in content_layers]
style_output=[vgg.get_layer(layer).output for layer in style_layers]
model_output= style_output+content_output
return models.Model(vgg.input,model_output)
def get_content_loss(noise,target):
loss = tf.reduce_mean(tf.square(noise-target))
return loss
def gram_matrix(tensor):
channels=int(tensor.shape[-1])
vector=tf.reshape(tensor,[-1,channels])
n=tf.shape(vector)[0]
gram_matrix=tf.matmul(vector,vector,transpose_a=True)
return gram_matrix/tf.cast(n,tf.float32)
def get_style_loss(noise,target):
gram_noise=gram_matrix(noise)
#gram_target=gram_matrix(target)
loss=tf.reduce_mean(tf.square(target-gram_noise))
return loss
def get_features(model,content_path,style_path):
content_img=img_preprocess(content_path)
style_image=img_preprocess(style_path)
content_output=model(content_img)
style_output=model(style_image)
content_feature = [layer[0] for layer in content_output[number_style:]]
style_feature = [layer[0] for layer in style_output[:number_style]]
return content_feature,style_feature
def compute_loss(model, loss_weights,image, gram_style_features, content_features):
style_weight,content_weight = loss_weights #style weight and content weight are user given parameters
#that define what percentage of content and/or style will be preserved in the generated image
output=model(image)
content_loss=0
style_loss=0
noise_style_features = output[:number_style]
noise_content_feature = output[number_style:]
weight_per_layer = 1.0/float(number_style)
for a,b in zip(gram_style_features,noise_style_features):
style_loss+=weight_per_layer*get_style_loss(b[0],a)
weight_per_layer =1.0/ float(number_content)
for a,b in zip(noise_content_feature,content_features):
content_loss+=weight_per_layer*get_content_loss(a[0],b)
style_loss *= style_weight
content_loss *= content_weight
total_loss = content_loss + style_loss
return total_loss,style_loss,content_loss
def compute_grads(dictionary):
with tf.GradientTape() as tape:
all_loss=compute_loss(**dictionary)
total_loss=all_loss[0]
return tape.gradient(total_loss,dictionary['image']),all_loss
def run_style_transfer(content_path,style_path,epochs=20,content_weight=1e3, style_weight=1e-2):
model=get_model()
for layer in model.layers:
layer.trainable = False
content_feature,style_feature = get_features(model,content_path,style_path)
style_gram_matrix=[gram_matrix(feature) for feature in style_feature]
noise = img_preprocess(content_path)
noise=tf.Variable(noise,dtype=tf.float32)
optimizer = tf.keras.optimizers.Adam(learning_rate=5, beta_1=0.99, epsilon=1e-1)
best_loss,best_img=float('inf'),None
loss_weights = (style_weight, content_weight)
dictionary={'model':model,
'loss_weights':loss_weights,
'image':noise,
'gram_style_features':style_gram_matrix,
'content_features':content_feature}
norm_means = np.array([103.939, 116.779, 123.68])
min_vals = -norm_means
max_vals = 255 - norm_means
imgs = []
for i in range(1,epochs+1):
grad,all_loss=compute_grads(dictionary)
total_loss,style_loss,content_loss=all_loss
optimizer.apply_gradients([(grad,noise)])
clipped=tf.clip_by_value(noise,min_vals,max_vals)
noise.assign(clipped)
if total_loss<best_loss:
best_loss = total_loss
best_img = deprocess_img(noise.numpy())
#for visualization
if i%1==0:
plot_img = noise.numpy()
plot_img = deprocess_img(plot_img)
imgs.append(plot_img)
return best_img,best_loss,imgs
content_path = "3.jpg"
style_path = "4.jpg"
def predict(image1_input, image2_input):
return run_style_transfer(image1_input,image2_input,epochs=60)[0]
image1_input = gr.inputs.Image(label="Image 1")
image2_input = gr.inputs.Image(label="Image 2")
output_image = gr.outputs.Image(label="Merged Image", type="filepath")
title = "Image Merger"
description = "Merge two input images"
gr.Interface(fn=predict, inputs=[image1_input, image2_input], outputs=output_image, title=title, description=description).launch()