import torch import torch.nn as nn import torch.optim as optim import torchvision.transforms as transforms import torchvision.models as models from torchvision.utils import save_image import numpy as np import gradio as gr IMAGE_SIZE = 244 # VGG image input size - we use VGG 19 as our pretrained CNN device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") cnn = models.vgg19(weights=None) state_dict = torch.load("vgg19-dcbb9e9d.pth") cnn.load_state_dict(state_dict) class VGG(nn.Module): def __init__(self): super(VGG, self).__init__() self.layers = ['0', '5', '10', '19', '28'] # layers we use as representations self.model = cnn.features[:29] # we don't care about later layers def forward(self, x): features = [] for layer_num, layer in enumerate(self.model): x = layer(x) # we don't care about the model output - we care about the output of individual layers if str(layer_num) in self.layers: features.append(x) return features gradio_transforms = transforms.Compose([ transforms.ToTensor(), transforms.Resize([IMAGE_SIZE, IMAGE_SIZE]) ]) def sanitize_inputs(epochs, lr, cl, sl): if epochs < 1: return ["Epochs must be positive", None] if not isinstance(epochs, int): return ["Epochs must be an integer", None] if lr < 0: return ["Learning rate must be positive", None] if lr > 1: return ["Learning rate must be less than one", None] if cl < 0 or cl > 1: return ["Content loss weight must be between 0 and 1", None] if sl < 0 or sl > 1: return ["Style loss weight must be between 0 and 1", None] return None def train(Epochs, Learning_Rate, Content_Loss, Style_Loss, Content_Image, Style_Image): errors = sanitize_inputs(Epochs, Learning_Rate, Content_Loss, Style_Loss) if errors is not None: return errors test = Content_Image content = gradio_transforms(Content_Image).unsqueeze(0).to(device) style = gradio_transforms(Style_Image).unsqueeze(0).to(device) generated = content.clone().requires_grad_(True).to(device) model = VGG().to(device).eval() optimizer = optim.Adam([generated], lr=Learning_Rate) for epoch in range(Epochs): generatedFeatures = model(generated) contentFeatures = model(content) styleFeatures = model(style) styleLoss = 0 contentLoss = 0 for genFeat, contFeat, styleFeat in zip(generatedFeatures, contentFeatures, styleFeatures): batch_size, channel, height, width = genFeat.shape contentLoss += torch.mean((genFeat - contFeat) ** 2) G = genFeat.view(channel, height * width).mm(genFeat.view(channel, height * width).t()) A = styleFeat.view(channel, height * width).mm(styleFeat.view(channel, height * width).t()) styleLoss += torch.mean((G - A) ** 2) total_loss = Content_Loss * contentLoss + Style_Loss * styleLoss optimizer.zero_grad() total_loss.backward() optimizer.step() save_image(generated, "generated_gradio.png") return ["No errors! Enjoy your new image!", "generated_gradio.png"] demo = gr.Interface( fn=train, inputs=["number", "number", "number", "number", "image", "image"], outputs=[ gr.Label(label="Error Messages"), gr.Image(label="Generated Image"), ], title="Neural Style Transfer", description="Perform neural style transfer on images of your choice! Provide a content image that contains the content you want to transform and a style image that contains the style you want to emulate.\n\nNote: Huggingface requires users to pay to gain access to GPUs, so this model is hosted on a cpu. Training for many epochs will take a VERY long time. Using a larger learning rate (e.g., 0.01) can help reduce the number of epochs you need.", theme=gr.themes.Soft() ) demo.launch(debug=True)